From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: =?ISO-8859-1?Q?R=FCdiger?= Sonderfeld Newsgroups: gmane.emacs.devel Subject: [PATCH] Added inotify support. Date: Mon, 01 Oct 2012 16:09:55 +0200 Message-ID: <2181827.T3JxG88qQt@descartes> References: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable X-Trace: ger.gmane.org 1349100642 18252 80.91.229.3 (1 Oct 2012 14:10:42 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Mon, 1 Oct 2012 14:10:42 +0000 (UTC) Cc: Leo , emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Mon Oct 01 16:10:46 2012 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 1TIghi-0005aA-LD for ged-emacs-devel@m.gmane.org; Mon, 01 Oct 2012 16:10:38 +0200 Original-Received: from localhost ([::1]:55608 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TIghc-0000yJ-Ss for ged-emacs-devel@m.gmane.org; Mon, 01 Oct 2012 10:10:32 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:48426) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TIghT-0000wq-Vc for emacs-devel@gnu.org; Mon, 01 Oct 2012 10:10:31 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1TIghL-00034P-P9 for emacs-devel@gnu.org; Mon, 01 Oct 2012 10:10:23 -0400 Original-Received: from ptmx.org ([178.63.28.110]:41700) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1TIghL-00030D-CY for emacs-devel@gnu.org; Mon, 01 Oct 2012 10:10:15 -0400 Original-Received: from localhost (localhost [127.0.0.1]) by ptmx.org (Postfix) with ESMTP id 861CE20BAD; Mon, 1 Oct 2012 16:10:11 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at ptmx.org Original-Received: from ptmx.org ([127.0.0.1]) by localhost (ptmx.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qnRWl3zHhqnP; Mon, 1 Oct 2012 16:10:05 +0200 (CEST) Original-Received: from descartes.localnet (chello080108246092.7.14.vie.surfer.at [80.108.246.92]) by ptmx.org (Postfix) with ESMTPSA id B3F7721B29; Mon, 1 Oct 2012 16:10:04 +0200 (CEST) User-Agent: KMail/4.8.5 (Linux/3.2.0-31-generic; KDE/4.8.5; x86_64; ; ) In-Reply-To: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-Received-From: 178.63.28.110 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:153825 Archived-At: On Monday 01 October 2012 00:38:09 Stefan Monnier wrote: > If there's a good chance this won't work without breaking compatibili= ty, > maybe a better option is to provide a low-level API that maps very > closely to inotify and then an Elisp layer on top which abstracts awa= y > differences between different systems. In that case we can install t= he > inotify support right away while we're still experimenting with the > higher-level abstraction. That's probably the best approach here. I changed the patch to provide= a low level inotify interface. However I did not provide an inotify_init(2) = like=20 function and instead initialization and closing of the inotify handle i= s done internally. I don't think that this should be exposed to elisp even in= the=20 low level interface. > But if they're unlikely to be important in practice, then > I guess the current solution might be acceptable. I think we are safe. I added that `equal' should be used to compare co= okies. =20 So we can easily change it without breaking the API. > I think the cleaner option is to define a new object type for it. > It could be either a new Lisp_Misc type, so you can make them print a= s > something like "#" (take a look at "enum > Lisp_Misc_Type" and "union Lisp_Misc" in src/lisp.h for starters; add= ing > a new type will require adding corresponding branches to the switch > statements in alloc.c and in print.c). That sounds like the best option. I haven't implemented it yet. Is it= =20 possible to make the Lisp_Misc_Type comparable with `equal'? Because th= e=20 watch-descriptor has to be comparable. Regards, R=C3=BCdiger -- >8 -- Inotify is a Linux kernel API to monitor file system events. This patch adds a basic inotify based API to Emacs: `inotify-add-watch' is based on inotify_add_watch(2). But uses callbacks and accepts lisp objects instead of a bit mask. `inotify-rm-watch' is based on inotify_rm_watch(2). The creation and destruction of an inotify fd with inotify_init1(2) is handled internally. Example: (let ((wd (inotify-add-watch "foo.txt" t (lambda (ev) (message "FS Event %s" ev))))) ;; ... (inotify-rm-watch wd)) Signed-off-by: R=C3=BCdiger Sonderfeld --- configure.ac | 13 ++ lisp/subr.el | 21 ++ src/Makefile.in | 2 +- src/emacs.c | 4 + src/inotify.c | 431=20 +++++++++++++++++++++++++++++++++++++++++ src/keyboard.c | 28 +++ src/lisp.h | 5 + src/termhooks.h | 5 + test/automated/inotify-test.el | 60 ++++++ 9 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 src/inotify.c create mode 100644 test/automated/inotify-test.el diff --git a/configure.ac b/configure.ac index 5a3aea7..c1ce23f 100644 --- a/configure.ac +++ b/configure.ac @@ -184,6 +184,7 @@ OPTION_DEFAULT_ON([gconf],[don't compile with GConf= =20 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)=20= support]) =20 ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -2101,6 +2102,18 @@ fi AC_SUBST(LIBGNUTLS_LIBS) AC_SUBST(LIBGNUTLS_CFLAGS) =20 +dnl inotify is only available on GNU/Linux. +HAVE_INOTIFY=3Dno +if test "${with_inotify}" =3D "yes"; then + AC_CHECK_HEADERS(sys/inotify.h) + if test "$ac_cv_header_sys_inotify_h" =3D yes ; then + AC_CHECK_FUNCS(inotify_init1 inotify_add_watch inotify_rm_watch,=20= HAVE_INOTIFY=3Dyes) + fi +fi +if test "${HAVE_INOTIFY}" =3D "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=3Dno diff --git a/lisp/subr.el b/lisp/subr.el index 8dfe78d..134a1dc 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -4159,6 +4159,27 @@ convenience wrapper around `make-progress-report= er' and=20 friends. nil ,@(cdr (cdr spec))))) =20 =0C +;;;; Support for watching filesystem events. + +(defun inotify-event-p (event) + "Check if EVENT is an inotify event." + (and (listp event) + (>=3D (length event) 3) + (eq (car event) 'inotify-event))) + +;;;###autoload +(defun inotify-handle-event (event) + "Handle file system monitoring event. +If EVENT is a filewatch event then the callback is called. If EVENT i= s +not a filewatch event then a `filewatch-error' is signaled." + (interactive "e") + + (unless (inotify-event-p event) + (signal 'inotify-error (cons "Not a valid inotify event" event))) + + (funcall (nth 2 event) (nth 1 event))) + +=0C ;;;; Comparing version strings. =20 (defconst version-separator "." diff --git a/src/Makefile.in b/src/Makefile.in index f8da009..1492dc6 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -339,7 +339,7 @@ base_obj =3D dispnew.o frame.o scroll.o xdisp.o men= u.o=20 $(XMENU_OBJ) window.o \ =09syntax.o $(UNEXEC_OBJ) bytecode.o \ =09process.o gnutls.o callproc.o \ =09region-cache.o sound.o atimer.o \ -=09doprnt.o intervals.o textprop.o composite.o xml.o \ +=09doprnt.o intervals.o textprop.o composite.o xml.o inotify.o \ =09profiler.o \ =09$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \ =09$(WINDOW_SYSTEM_OBJ) diff --git a/src/emacs.c b/src/emacs.c index 05affee..e43fa0e 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1411,6 +1411,10 @@ Using an Emacs configured with --with-x-toolkit=3D= lucid=20 does not have this problem syms_of_gnutls (); #endif =20 +#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 0000000..49a45cb --- /dev/null +++ b/src/inotify.c @@ -0,0 +1,431 @@ +/* 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 */ + +static Lisp_Object Qinotify_event; + +#include +#include + +enum { uninitialized =3D -100 }; +/* File handle for inotify. */ +static int inotifyfd =3D 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 =3D Qnil; + if (mask & IN_ACCESS) + aspects =3D Fcons (Qaccess, aspects); + if (mask & IN_ATTRIB) + aspects =3D Fcons (Qattrib, aspects); + if (mask & IN_CLOSE_WRITE) + aspects =3D Fcons (Qclose_write, aspects); + if (mask & IN_CLOSE_NOWRITE) + aspects =3D Fcons (Qclose_nowrite, aspects); + if (mask & IN_CREATE) + aspects =3D Fcons (Qcreate, aspects); + if (mask & IN_DELETE) + aspects =3D Fcons (Qdelete, aspects); + if (mask & IN_DELETE_SELF) + aspects =3D Fcons (Qdelete_self, aspects); + if (mask & IN_MODIFY) + aspects =3D Fcons (Qmodify, aspects); + if (mask & IN_MOVE_SELF) + aspects =3D Fcons (Qmove_self, aspects); + if (mask & IN_MOVED_FROM) + aspects =3D Fcons (Qmoved_from, aspects); + if (mask & IN_MOVED_TO) + aspects =3D Fcons (Qmoved_to, aspects); + if (mask & IN_OPEN) + aspects =3D Fcons (Qopen, aspects); + if (mask & IN_IGNORED) + aspects =3D Fcons (Qignored, aspects); + if (mask & IN_ISDIR) + aspects =3D Fcons (Qisdir, aspects); + if (mask & IN_Q_OVERFLOW) + aspects =3D Fcons (Qq_overflow, aspects); + if (mask & IN_UNMOUNT) + aspects =3D Fcons (Qunmount, aspects); + return aspects; +} + +static Lisp_Object +inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event = const=20 *ev) +{ + Lisp_Object name =3D Qnil; + if (ev->len > 0) + { + size_t const len =3D strlen (ev->name); + name =3D make_unibyte_string (ev->name, min (len, ev->len)); + name =3D 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 ino= tify + 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 =3D 0; + if (ioctl (fd, FIONREAD, &to_read) =3D=3D -1) + report_file_error ("Error while trying to retrieve file system eve= nts", + 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 event= s", + Qnil); + } + + EVENT_INIT (event); + event.kind =3D INOTIFY_EVENT; + event.arg =3D Qnil; + + i =3D 0; + while (i < (size_t)n) + { + struct inotify_event *ev =3D (struct inotify_event*)&buffer[i]; + + watch_object =3D Fassoc (make_watch_descriptor (ev->wd), watch_l= ist); + if (!NILP (watch_object)) + { + event.arg =3D inotifyevent_to_event (watch_object, ev); + + /* If event was removed automatically: Drop it from watch li= st. */ + if (ev->mask & IN_IGNORED) + watch_list =3D Fdelete (watch_object, watch_list); + } + + i +=3D 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 =3D aspect; + uint32_t mask =3D 0; + while (CONSP (x)) + { + mask |=3D symbol_to_inotifymask (XCAR (x)); + x =3D 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= =20 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 o= f an +event. It gets passed a single argument EVENT which contains an event= =20 structure +of the format + +(WATCH-DESCRIPTOR ASPECTS COOKIE NAME) + +WATCH-DESCRIPTOR is the same object that was returned by this function= . It=20 can +be tested for equality using `equal'. ASPECTS describes the event. I= t is a +list of ASPECT symbols described above and can also contain one of the= =20 following +symbols + +ignored +isdir +q-overflow +unmount + +COOKIE is an object that can be compared using `equal' to identify two= =20 matching +renames (moved-from and moved-to). + +If a directory is watched then NAME is the name of file that caused th= e=20 event. + +See inotify(7) and inotify_add_watch(2) for further information. The = inotify=20 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 =3D -1; + + CHECK_STRING (file_name); + + if (inotifyfd =3D=3D uninitialized) + { + inotifyfd =3D inotify_init1 (IN_NONBLOCK|IN_CLOEXEC); + if (inotifyfd =3D=3D -1) + { + inotifyfd =3D uninitialized; + report_file_error ("File watching feature (inotify) is not=20= available", + Qnil); + } + watch_list =3D Qnil; + add_read_fd (inotifyfd, &inotify_callback, NULL); + } + + mask =3D aspect_to_inotifymask (aspect); + decoded_file_name =3D ENCODE_FILE (file_name); + watchdesc =3D inotify_add_watch (inotifyfd, SSDATA (decoded_file_nam= e),=20 mask); + if (watchdesc =3D=3D -1) + report_file_error ("Could not add watch for file", Fcons (file_nam= e,=20 Qnil)); + + watch_descriptor =3D make_watch_descriptor (watchdesc); + + /* Delete existing watch object. */ + watch_object =3D Fassoc (watch_descriptor, watch_list); + if (!NILP (watch_object)) + watch_list =3D Fdelete (watch_object, watch_list); + + /* Store watch object in watch list. */ + watch_object =3D Fcons (watch_descriptor, callback); + watch_list =3D 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 =3D XINT (watch_descriptor); + + if (inotify_rm_watch (inotifyfd, wd) =3D=3D -1) + report_file_error ("Could not rm watch", Fcons (watch_descriptor, + Qnil)); + + /* Remove watch descriptor from watch list. */ + watch_object =3D Fassoc (watch_descriptor, watch_list); + if (!NILP (watch_object)) + watch_list =3D Fdelete (watch_object, watch_list); + + /* Cleanup if no more files are watched. */ + if (NILP (watch_list)) + { + close (inotifyfd); + delete_read_fd (inotifyfd); + inotifyfd =3D 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"); + + DEFSYM (Qinotify_event, "inotify-event"); + + 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 f3d7df5..02e1473 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -321,6 +321,9 @@ static Lisp_Object Qsave_session; #ifdef HAVE_DBUS static Lisp_Object Qdbus_event; #endif +#ifdef HAVE_INOTIFY +static Lisp_Object Qinotify_event; +#endif /* HAVE_INOTIFY */ static Lisp_Object Qconfig_changed_event; =20 /* Lisp_Object Qmouse_movement; - also an event header */ @@ -4021,6 +4024,13 @@ kbd_buffer_get_event (KBOARD **kbp, =09 kbd_fetch_ptr =3D event + 1; =09} #endif +#ifdef HAVE_INOTIFY + else if (event->kind =3D=3D INOTIFY_EVENT) + { + obj =3D make_lispy_event (event); + kbd_fetch_ptr =3D event + 1; + } +#endif else if (event->kind =3D=3D CONFIG_CHANGED_EVENT) =09{ =09 obj =3D make_lispy_event (event); @@ -5938,6 +5948,13 @@ make_lispy_event (struct input_event *event) } #endif /* HAVE_DBUS */ =20 +#ifdef HAVE_INOTIFY + case INOTIFY_EVENT: + { + return Fcons (Qinotify_event, event->arg); + } +#endif /* HAVE_INOTIFY */ + case CONFIG_CHANGED_EVENT: =09return Fcons (Qconfig_changed_event, Fcons (event->arg, @@ -11408,6 +11425,10 @@ syms_of_keyboard (void) DEFSYM (Qdbus_event, "dbus-event"); #endif =20 +#ifdef HAVE_INOTIFY + DEFSYM (Qinotify_event, "inotify-event"); +#endif /* HAVE_INOTIFY */ + DEFSYM (QCenable, ":enable"); DEFSYM (QCvisible, ":visible"); DEFSYM (QChelp, ":help"); @@ -12168,6 +12189,13 @@ keys_of_keyboard (void) =09=09=09 "dbus-handle-event"); #endif =20 +#ifdef HAVE_INOTIFY + /* Define a special event which is raised for inotify callback + functions. */ + initial_define_lispy_key (Vspecial_event_map, "inotify-event", + "inotify-handle-event"); +#endif /* HAVE_INOTIFY */ + initial_define_lispy_key (Vspecial_event_map, "config-changed-event"= , =09=09=09 "ignore"); #if defined (WINDOWSNT) diff --git a/src/lisp.h b/src/lisp.h index c3cabe0..02bfa08 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3497,6 +3497,11 @@ extern void syms_of_fontset (void); extern Lisp_Object Qfont_param; #endif =20 +/* 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 f35bd92..5890d10 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -211,6 +211,11 @@ enum event_kind , NS_NONKEY_EVENT #endif =20 +#ifdef HAVE_INOTIFY + /* File or directory was changed. */ + , INOTIFY_EVENT +#endif + }; =20 /* If a struct input_event has a kind which is SELECTION_REQUEST_EVENT= diff --git a/test/automated/inotify-test.el b/test/automated/inotify-te= st.el new file mode 100644 index 0000000..edda7ef --- /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=C3=BCdiger 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 b= y +;; 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) ni= l) + ;; :type 'error))) + + (ert-deftest inotify-file-watch-simple () + "Test if watching a normal file works." + (let ((temp-file (make-temp-file "inotify-simple")) +=09 (events 0)) + (let ((wd +=09 (inotify-add-watch temp-file t (lambda (ev) + (setq events (1+ events)= ))))) +=09(unwind-protect +=09 (progn +=09 (with-temp-file temp-file +=09=09(insert "Foo\n")) +=09 (sit-for 5) ;; Hacky. Wait for 5s until events are processed +=09 (should (> events 0))) +=09 (inotify-rm-watch wd))))) +) + +(provide 'inotify-tests) +;;; inotify-tests.el ends here. --=20 1.7.11.3