diff options
| author | Michael Albinus | 2015-11-25 15:24:27 +0100 |
|---|---|---|
| committer | Michael Albinus | 2015-11-25 15:24:27 +0100 |
| commit | 91cad2b327f19094764ca1dc2c432368742f1d2f (patch) | |
| tree | c08880452d912fb7992d631eb092b1943d65b1b0 /src | |
| parent | c378d6c33f751d1a0b97958f3cacfe0b07c72f58 (diff) | |
| parent | bec57a486a2a40d7c770dab72a34cf6a4d17a5d0 (diff) | |
| download | emacs-91cad2b327f19094764ca1dc2c432368742f1d2f.tar.gz emacs-91cad2b327f19094764ca1dc2c432368742f1d2f.zip | |
Merge from scratch/kqueue
bec57a4 Some final fixes in file notification before merging with master
0247489 Rework file notifications, kqueue has problems with directory monitors
5154781 Continie with pending events
6b490c0 Improve loops in file-notify-test06-many-events
c8e266f Handle more complex rename operation in kqueue
5044bdf New test with a larger number of events.
65ba5a9 Further fixes for kqueue.
13f3508 Code cleanup of kqueue.c
99aa855 Doc changes for kqueue
8deebe1 Finish implementation in kqueue.c
90d6c69 * lisp/filenotify.el (file-notify-add-watch): Fix thinko.
e95b309 More work on kqueue
41d9bd0 Implement directory events
c571fc1 Build fixes for kqueue support.
e0a68f2 Continue kqueue implementation
7543d1c Work on kqueue
e3354e2 Add kqueue support
c6457ce Minor fix to comment indentation and typo in last commit
b92307f linum-mode plays more nicely with other margin-setting extensions
58e6235 * lisp/image-mode.el: Support encrypted file
9375652 * lisp/progmodes/verilog-mode.el (verilog-save-buffer-state): Add backquote
47f83b6 ; ChangeLog.2 fixes
7cc233e * lisp/emacs-lisp/package.el: Fix a decoding issue
5f9153f * lisp/emacs-lisp/package.el: Refactor -with-work-buffer-async
353f5e7 * lisp/progmodes/verilog-mode.el: Use with-silent-modifications
70f1fda ; Auto-commit of ChangeLog files.
ae0653b * CONTRIBUTE: Remove information about feature freeze.
9459456 Merge branch 'release-process-lowercase'
9a4aa0f Document the release process
f8cc14b * admin/release-process: Rename from admin/FOR-RELEASE.
dcd5877 gitmerge: Fix git log command
2ac79ae gitmerge: Try to detect cherry-picks
5f7a2a9 Increment Emacs version on master branch
ed2e7e2 Mention CONTRIBUTE in README
9e00a02 Update verilog-mode.el to 2015-11-09-b121d60-vpo.
138ad3d ; Fix warnings
7126e9a ; Update xref-etags-mode for the latest change
246d660 Use generic dispatch for xref backends
31f6e93 Support rectangular regions for more commands
f103a27 Handle multiple matches on the same line; add highlighting
fe973fc Replace xref-match-bounds with xref-match-length
92a5010 Merge from gnulib
04ac097 Spruce up ftfont.c memory allocation
4c4b520 Port recent XCB changes to 64-bit ‘long int’
4f0ce9c * src/undo.c (run_undoable_change): Now static.
695a6f9 Remove support for ':timeout' from w32 tray notifications
a731c2f * test/automated/simple-test.el: Add test for bug#20698 (bug#21885)
2b4c0c0 * lisp/progmodes/elisp-mode.el: Declare function `project-roots'
66b9f7b * src/undo.c: Small fixes for previous change
2fac30e Add a few more variables to redisplay--variables
04f69f1 * lisp/loadup.el: Enlarge the size of the hash table to 80000.
e221d32 Fix point positioning after transposing with negative arg
35f5afb Fix last change in shr.el
508e77b Fix last change
d60ed3f Another fix for MinGW64 and Cygwin builds due to notifications
805a39b Remove intern calls and XXX comments from Fx_export_frames
9463abf shr: don't invoke unbound function (Bug#21895)
6e5186e * test/automated/keymaps-test.el: Fix test to make it repeatable
0c92826 * test/automated/cl-lib-tests.el (cl-lib-struct-constructors): Small fix
39dbd1c : Tests for undo-auto functionality.
20aa42e ; Merge branch 'fix/no-undo-boundary-on-secondary-buffer-change'
44dfa86 The heuristic that Emacs uses to add an `undo-boundary' has been reworked, as it interacts poorly with functions on `post-command-hook' or `after-change-functions'.
d2f73db Bind [?\S-\ ] to previous line command in Dired-like modes.
c1bc6e5 Fix the MinGW64 and Cygwin-w32 builds
1e363a8 Enable sorting of JSON object keys when encoding
9dd7da9 * test/automated/keymap-tests.el: New test file
aa17de9 Speed up x_real_pos_and_offsets using XCB
a838c83 Enable use of XCB for checking window manager state
c7f2b6a Detect XCB and save a connection handle
e1c27db Reduce some data dependencies between X calls
25e32bd Use color cache for creating bitmap
851be0f Add "^" to the interactive specs of `dired-next/previous-line'
055ca3a Sync with soap-client repository, version 3.0.2
e0f64e7 CC Mode: Respect users' settings of open-paren-in-column-0-is-defun-start.
952395d * lisp/obarray.el: Fix shadowed variables
436d330 Avoid error in submitting a form with EWW
e887f6e ; * doc/lispref/os.texi: Fix indentation of sample code.
51d840a Rename seq-p and map-p to seqp and mapp
23036ba Rename obarray-p to obarrayp
20aea42 Rename obarray-foreach to obarray-map
a3b2101 New file with obarray functions.
9d43941 Implement tray notifications for MS-Windows
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.in | 11 | ||||
| -rw-r--r-- | src/emacs.c | 16 | ||||
| -rw-r--r-- | src/inotify.c | 9 | ||||
| -rw-r--r-- | src/keyboard.c | 4 | ||||
| -rw-r--r-- | src/kqueue.c | 520 | ||||
| -rw-r--r-- | src/lisp.h | 16 |
6 files changed, 556 insertions, 20 deletions
diff --git a/src/Makefile.in b/src/Makefile.in index d667c55ee33..d7ad3954579 100644 --- a/src/Makefile.in +++ b/src/Makefile.in | |||
| @@ -160,12 +160,13 @@ SETTINGS_LIBS = @SETTINGS_LIBS@ | |||
| 160 | ## gtkutil.o if USE_GTK, else empty. | 160 | ## gtkutil.o if USE_GTK, else empty. |
| 161 | GTK_OBJ=@GTK_OBJ@ | 161 | GTK_OBJ=@GTK_OBJ@ |
| 162 | 162 | ||
| 163 | ## gfilenotify.o if HAVE_GFILENOTIFY. | ||
| 164 | ## inotify.o if HAVE_INOTIFY. | 163 | ## inotify.o if HAVE_INOTIFY. |
| 164 | ## kqueue.o if HAVE_KQUEUE. | ||
| 165 | ## gfilenotify.o if HAVE_GFILENOTIFY. | ||
| 165 | ## w32notify.o if HAVE_W32NOTIFY. | 166 | ## w32notify.o if HAVE_W32NOTIFY. |
| 166 | NOTIFY_OBJ = @NOTIFY_OBJ@ | 167 | NOTIFY_OBJ = @NOTIFY_OBJ@ |
| 167 | GFILENOTIFY_CFLAGS = @GFILENOTIFY_CFLAGS@ | 168 | NOTIFY_CFLAGS = @NOTIFY_CFLAGS@ |
| 168 | GFILENOTIFY_LIBS = @GFILENOTIFY_LIBS@ | 169 | NOTIFY_LIBS = @NOTIFY_LIBS@ |
| 169 | 170 | ||
| 170 | ## -ltermcap, or -lncurses, or -lcurses, or "". | 171 | ## -ltermcap, or -lncurses, or -lcurses, or "". |
| 171 | LIBS_TERMCAP=@LIBS_TERMCAP@ | 172 | LIBS_TERMCAP=@LIBS_TERMCAP@ |
| @@ -355,7 +356,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ | |||
| 355 | $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ | 356 | $(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \ |
| 356 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ | 357 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ |
| 357 | $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ | 358 | $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ |
| 358 | $(LIBGNUTLS_CFLAGS) $(GFILENOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ | 359 | $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ |
| 359 | $(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS) | 360 | $(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS) |
| 360 | ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS) | 361 | ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS) |
| 361 | 362 | ||
| @@ -468,7 +469,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ | |||
| 468 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ | 469 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ |
| 469 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ | 470 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ |
| 470 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) \ | 471 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) \ |
| 471 | $(GFILENOTIFY_LIBS) $(LIB_MATH) $(LIBZ) | 472 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) |
| 472 | 473 | ||
| 473 | $(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT) | 474 | $(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT) |
| 474 | $(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)" | 475 | $(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)" |
diff --git a/src/emacs.c b/src/emacs.c index b4052b851d7..2e9f950851a 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -1350,6 +1350,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1350 | tzset (); | 1350 | tzset (); |
| 1351 | #endif /* MSDOS */ | 1351 | #endif /* MSDOS */ |
| 1352 | 1352 | ||
| 1353 | #ifdef HAVE_KQUEUE | ||
| 1354 | globals_of_kqueue (); | ||
| 1355 | #endif | ||
| 1356 | |||
| 1353 | #ifdef HAVE_GFILENOTIFY | 1357 | #ifdef HAVE_GFILENOTIFY |
| 1354 | globals_of_gfilenotify (); | 1358 | globals_of_gfilenotify (); |
| 1355 | #endif | 1359 | #endif |
| @@ -1520,14 +1524,18 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1520 | 1524 | ||
| 1521 | syms_of_gnutls (); | 1525 | syms_of_gnutls (); |
| 1522 | 1526 | ||
| 1523 | #ifdef HAVE_GFILENOTIFY | ||
| 1524 | syms_of_gfilenotify (); | ||
| 1525 | #endif /* HAVE_GFILENOTIFY */ | ||
| 1526 | |||
| 1527 | #ifdef HAVE_INOTIFY | 1527 | #ifdef HAVE_INOTIFY |
| 1528 | syms_of_inotify (); | 1528 | syms_of_inotify (); |
| 1529 | #endif /* HAVE_INOTIFY */ | 1529 | #endif /* HAVE_INOTIFY */ |
| 1530 | 1530 | ||
| 1531 | #ifdef HAVE_KQUEUE | ||
| 1532 | syms_of_kqueue (); | ||
| 1533 | #endif /* HAVE_KQUEUE */ | ||
| 1534 | |||
| 1535 | #ifdef HAVE_GFILENOTIFY | ||
| 1536 | syms_of_gfilenotify (); | ||
| 1537 | #endif /* HAVE_GFILENOTIFY */ | ||
| 1538 | |||
| 1531 | #ifdef HAVE_DBUS | 1539 | #ifdef HAVE_DBUS |
| 1532 | syms_of_dbusbind (); | 1540 | syms_of_dbusbind (); |
| 1533 | #endif /* HAVE_DBUS */ | 1541 | #endif /* HAVE_DBUS */ |
diff --git a/src/inotify.c b/src/inotify.c index d1a80bbad1b..6577ee28cd1 100644 --- a/src/inotify.c +++ b/src/inotify.c | |||
| @@ -46,8 +46,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 46 | static int inotifyfd = -1; | 46 | static int inotifyfd = -1; |
| 47 | 47 | ||
| 48 | /* Assoc list of files being watched. | 48 | /* Assoc list of files being watched. |
| 49 | Format: | 49 | Format: (watch-descriptor name callback) |
| 50 | (watch-descriptor . callback) | ||
| 51 | */ | 50 | */ |
| 52 | static Lisp_Object watch_list; | 51 | static Lisp_Object watch_list; |
| 53 | 52 | ||
| @@ -106,12 +105,14 @@ inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev) | |||
| 106 | name = make_unibyte_string (ev->name, min (len, ev->len)); | 105 | name = make_unibyte_string (ev->name, min (len, ev->len)); |
| 107 | name = DECODE_FILE (name); | 106 | name = DECODE_FILE (name); |
| 108 | } | 107 | } |
| 108 | else | ||
| 109 | name = XCAR (XCDR (watch_object)); | ||
| 109 | 110 | ||
| 110 | return list2 (list4 (make_watch_descriptor (ev->wd), | 111 | return list2 (list4 (make_watch_descriptor (ev->wd), |
| 111 | mask_to_aspects (ev->mask), | 112 | mask_to_aspects (ev->mask), |
| 112 | name, | 113 | name, |
| 113 | make_number (ev->cookie)), | 114 | make_number (ev->cookie)), |
| 114 | XCDR (watch_object)); | 115 | Fnth (make_number (2), watch_object)); |
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | /* This callback is called when the FD is available for read. The inotify | 118 | /* This callback is called when the FD is available for read. The inotify |
| @@ -325,7 +326,7 @@ is managed internally and there is no corresponding inotify_init. Use | |||
| 325 | watch_list = Fdelete (watch_object, watch_list); | 326 | watch_list = Fdelete (watch_object, watch_list); |
| 326 | 327 | ||
| 327 | /* Store watch object in watch list. */ | 328 | /* Store watch object in watch list. */ |
| 328 | watch_object = Fcons (watch_descriptor, callback); | 329 | watch_object = list3 (watch_descriptor, encoded_file_name, callback); |
| 329 | watch_list = Fcons (watch_object, watch_list); | 330 | watch_list = Fcons (watch_object, watch_list); |
| 330 | 331 | ||
| 331 | return watch_descriptor; | 332 | return watch_descriptor; |
diff --git a/src/keyboard.c b/src/keyboard.c index 2449abb7dfc..ab7cb34a030 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -5945,12 +5945,12 @@ make_lispy_event (struct input_event *event) | |||
| 5945 | } | 5945 | } |
| 5946 | #endif /* HAVE_DBUS */ | 5946 | #endif /* HAVE_DBUS */ |
| 5947 | 5947 | ||
| 5948 | #if defined HAVE_GFILENOTIFY || defined HAVE_INOTIFY | 5948 | #if defined HAVE_INOTIFY || defined HAVE_KQUEUE || defined HAVE_GFILENOTIFY |
| 5949 | case FILE_NOTIFY_EVENT: | 5949 | case FILE_NOTIFY_EVENT: |
| 5950 | { | 5950 | { |
| 5951 | return Fcons (Qfile_notify, event->arg); | 5951 | return Fcons (Qfile_notify, event->arg); |
| 5952 | } | 5952 | } |
| 5953 | #endif /* defined HAVE_GFILENOTIFY || defined HAVE_INOTIFY */ | 5953 | #endif /* HAVE_INOTIFY || HAVE_KQUEUE || HAVE_GFILENOTIFY */ |
| 5954 | 5954 | ||
| 5955 | case CONFIG_CHANGED_EVENT: | 5955 | case CONFIG_CHANGED_EVENT: |
| 5956 | return list3 (Qconfig_changed_event, | 5956 | return list3 (Qconfig_changed_event, |
diff --git a/src/kqueue.c b/src/kqueue.c new file mode 100644 index 00000000000..1830040637e --- /dev/null +++ b/src/kqueue.c | |||
| @@ -0,0 +1,520 @@ | |||
| 1 | /* Filesystem notifications support with kqueue API. | ||
| 2 | Copyright (C) 2015 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This file is part of GNU Emacs. | ||
| 5 | |||
| 6 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 7 | it under the terms of the GNU General Public License as published by | ||
| 8 | the Free Software Foundation, either version 3 of the License, or | ||
| 9 | (at your option) any later version. | ||
| 10 | |||
| 11 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | GNU General Public License for more details. | ||
| 15 | |||
| 16 | You should have received a copy of the GNU General Public License | ||
| 17 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 18 | |||
| 19 | #include <config.h> | ||
| 20 | |||
| 21 | #ifdef HAVE_KQUEUE | ||
| 22 | #include <stdio.h> | ||
| 23 | #include <sys/types.h> | ||
| 24 | #include <sys/event.h> | ||
| 25 | #include <sys/time.h> | ||
| 26 | #include <sys/file.h> | ||
| 27 | #include "lisp.h" | ||
| 28 | #include "keyboard.h" | ||
| 29 | #include "process.h" | ||
| 30 | |||
| 31 | |||
| 32 | /* File handle for kqueue. */ | ||
| 33 | static int kqueuefd = -1; | ||
| 34 | |||
| 35 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]). */ | ||
| 36 | static Lisp_Object watch_list; | ||
| 37 | |||
| 38 | /* Generate a list from the directory_files_internal output. | ||
| 39 | Items are (INODE FILE-NAME LAST-MOD LAST-STATUS-MOD SIZE). */ | ||
| 40 | Lisp_Object | ||
| 41 | kqueue_directory_listing (Lisp_Object directory_files) | ||
| 42 | { | ||
| 43 | Lisp_Object dl, result = Qnil; | ||
| 44 | |||
| 45 | for (dl = directory_files; ! NILP (dl); dl = XCDR (dl)) { | ||
| 46 | /* We ignore "." and "..". */ | ||
| 47 | if ((strcmp (".", SSDATA (XCAR (XCAR (dl)))) == 0) || | ||
| 48 | (strcmp ("..", SSDATA (XCAR (XCAR (dl)))) == 0)) | ||
| 49 | continue; | ||
| 50 | |||
| 51 | result = Fcons | ||
| 52 | (list5 (/* inode. */ | ||
| 53 | Fnth (make_number (11), XCAR (dl)), | ||
| 54 | /* filename. */ | ||
| 55 | XCAR (XCAR (dl)), | ||
| 56 | /* last modification time. */ | ||
| 57 | Fnth (make_number (6), XCAR (dl)), | ||
| 58 | /* last status change time. */ | ||
| 59 | Fnth (make_number (7), XCAR (dl)), | ||
| 60 | /* size. */ | ||
| 61 | Fnth (make_number (8), XCAR (dl))), | ||
| 62 | result); | ||
| 63 | } | ||
| 64 | return result; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* Generate a file notification event. */ | ||
| 68 | static void | ||
| 69 | kqueue_generate_event | ||
| 70 | (Lisp_Object watch_object, Lisp_Object actions, | ||
| 71 | Lisp_Object file, Lisp_Object file1) | ||
| 72 | { | ||
| 73 | Lisp_Object flags, action, entry; | ||
| 74 | struct input_event event; | ||
| 75 | |||
| 76 | /* Check, whether all actions shall be monitored. */ | ||
| 77 | flags = Fnth (make_number (2), watch_object); | ||
| 78 | action = actions; | ||
| 79 | do { | ||
| 80 | if (NILP (action)) | ||
| 81 | break; | ||
| 82 | entry = XCAR (action); | ||
| 83 | if (NILP (Fmember (entry, flags))) { | ||
| 84 | action = XCDR (action); | ||
| 85 | actions = Fdelq (entry, actions); | ||
| 86 | } else | ||
| 87 | action = XCDR (action); | ||
| 88 | } while (1); | ||
| 89 | |||
| 90 | /* Store it into the input event queue. */ | ||
| 91 | if (! NILP (actions)) { | ||
| 92 | EVENT_INIT (event); | ||
| 93 | event.kind = FILE_NOTIFY_EVENT; | ||
| 94 | event.frame_or_window = Qnil; | ||
| 95 | event.arg = list2 (Fcons (XCAR (watch_object), | ||
| 96 | Fcons (actions, | ||
| 97 | NILP (file1) | ||
| 98 | ? Fcons (file, Qnil) | ||
| 99 | : list2 (file, file1))), | ||
| 100 | Fnth (make_number (3), watch_object)); | ||
| 101 | kbd_buffer_store_event (&event); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | /* This compares two directory listings in case of a `write' event for | ||
| 106 | a directory. Generate resulting file notification events. The old | ||
| 107 | directory listing is retrieved from watch_object, it will be | ||
| 108 | replaced by the new directory listing at the end of this | ||
| 109 | function. */ | ||
| 110 | static void | ||
| 111 | kqueue_compare_dir_list | ||
| 112 | (Lisp_Object watch_object) | ||
| 113 | { | ||
| 114 | Lisp_Object dir, pending_dl, deleted_dl; | ||
| 115 | Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; | ||
| 116 | |||
| 117 | dir = XCAR (XCDR (watch_object)); | ||
| 118 | pending_dl = Qnil; | ||
| 119 | deleted_dl = Qnil; | ||
| 120 | |||
| 121 | old_directory_files = Fnth (make_number (4), watch_object); | ||
| 122 | old_dl = kqueue_directory_listing (old_directory_files); | ||
| 123 | |||
| 124 | /* When the directory is not accessible anymore, it has been deleted. */ | ||
| 125 | if (NILP (Ffile_directory_p (dir))) { | ||
| 126 | kqueue_generate_event (watch_object, Fcons (Qdelete, Qnil), dir, Qnil); | ||
| 127 | return; | ||
| 128 | } | ||
| 129 | new_directory_files = | ||
| 130 | directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); | ||
| 131 | new_dl = kqueue_directory_listing (new_directory_files); | ||
| 132 | |||
| 133 | /* Parse through the old list. */ | ||
| 134 | dl = old_dl; | ||
| 135 | while (1) { | ||
| 136 | Lisp_Object old_entry, new_entry, dl1; | ||
| 137 | if (NILP (dl)) | ||
| 138 | break; | ||
| 139 | |||
| 140 | /* Search for an entry with the same inode. */ | ||
| 141 | old_entry = XCAR (dl); | ||
| 142 | new_entry = assq_no_quit (XCAR (old_entry), new_dl); | ||
| 143 | if (! NILP (Fequal (old_entry, new_entry))) { | ||
| 144 | /* Both entries are identical. Nothing to do. */ | ||
| 145 | new_dl = Fdelq (new_entry, new_dl); | ||
| 146 | goto the_end; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Both entries have the same inode. */ | ||
| 150 | if (! NILP (new_entry)) { | ||
| 151 | /* Both entries have the same file name. */ | ||
| 152 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 153 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 154 | /* Modification time has been changed, the file has been written. */ | ||
| 155 | if (NILP (Fequal (Fnth (make_number (2), old_entry), | ||
| 156 | Fnth (make_number (2), new_entry)))) | ||
| 157 | kqueue_generate_event | ||
| 158 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (old_entry)), Qnil); | ||
| 159 | /* Status change time has been changed, the file attributes | ||
| 160 | have changed. */ | ||
| 161 | if (NILP (Fequal (Fnth (make_number (3), old_entry), | ||
| 162 | Fnth (make_number (3), new_entry)))) | ||
| 163 | kqueue_generate_event | ||
| 164 | (watch_object, Fcons (Qattrib, Qnil), | ||
| 165 | XCAR (XCDR (old_entry)), Qnil); | ||
| 166 | |||
| 167 | } else { | ||
| 168 | /* The file has been renamed. */ | ||
| 169 | kqueue_generate_event | ||
| 170 | (watch_object, Fcons (Qrename, Qnil), | ||
| 171 | XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); | ||
| 172 | deleted_dl = Fcons (new_entry, deleted_dl); | ||
| 173 | } | ||
| 174 | new_dl = Fdelq (new_entry, new_dl); | ||
| 175 | goto the_end; | ||
| 176 | } | ||
| 177 | |||
| 178 | /* Search, whether there is a file with the same name but another | ||
| 179 | inode. */ | ||
| 180 | for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { | ||
| 181 | new_entry = XCAR (dl1); | ||
| 182 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 183 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 184 | pending_dl = Fcons (new_entry, pending_dl); | ||
| 185 | new_dl = Fdelq (new_entry, new_dl); | ||
| 186 | goto the_end; | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Check, whether this a pending file. */ | ||
| 191 | new_entry = assq_no_quit (XCAR (old_entry), pending_dl); | ||
| 192 | |||
| 193 | if (NILP (new_entry)) { | ||
| 194 | /* Check, whether this is an already deleted file (by rename). */ | ||
| 195 | for (dl1 = deleted_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { | ||
| 196 | new_entry = XCAR (dl1); | ||
| 197 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 198 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 199 | deleted_dl = Fdelq (new_entry, deleted_dl); | ||
| 200 | goto the_end; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | /* The file has been deleted. */ | ||
| 204 | kqueue_generate_event | ||
| 205 | (watch_object, Fcons (Qdelete, Qnil), XCAR (XCDR (old_entry)), Qnil); | ||
| 206 | |||
| 207 | } else { | ||
| 208 | /* The file has been renamed. */ | ||
| 209 | kqueue_generate_event | ||
| 210 | (watch_object, Fcons (Qrename, Qnil), | ||
| 211 | XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); | ||
| 212 | pending_dl = Fdelq (new_entry, pending_dl); | ||
| 213 | } | ||
| 214 | |||
| 215 | the_end: | ||
| 216 | dl = XCDR (dl); | ||
| 217 | old_dl = Fdelq (old_entry, old_dl); | ||
| 218 | } | ||
| 219 | |||
| 220 | /* Parse through the resulting new list. */ | ||
| 221 | dl = new_dl; | ||
| 222 | while (1) { | ||
| 223 | Lisp_Object entry; | ||
| 224 | if (NILP (dl)) | ||
| 225 | break; | ||
| 226 | |||
| 227 | /* A new file has appeared. */ | ||
| 228 | entry = XCAR (dl); | ||
| 229 | kqueue_generate_event | ||
| 230 | (watch_object, Fcons (Qcreate, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 231 | |||
| 232 | /* Check size of that file. */ | ||
| 233 | Lisp_Object size = Fnth (make_number (4), entry); | ||
| 234 | if (FLOATP (size) || (XINT (size) > 0)) | ||
| 235 | kqueue_generate_event | ||
| 236 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 237 | |||
| 238 | dl = XCDR (dl); | ||
| 239 | new_dl = Fdelq (entry, new_dl); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Parse through the resulting pending_dl list. */ | ||
| 243 | dl = pending_dl; | ||
| 244 | while (1) { | ||
| 245 | Lisp_Object entry; | ||
| 246 | if (NILP (dl)) | ||
| 247 | break; | ||
| 248 | |||
| 249 | /* A file is still pending. Assume it was a write. */ | ||
| 250 | entry = XCAR (dl); | ||
| 251 | kqueue_generate_event | ||
| 252 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 253 | |||
| 254 | dl = XCDR (dl); | ||
| 255 | pending_dl = Fdelq (entry, pending_dl); | ||
| 256 | } | ||
| 257 | |||
| 258 | /* At this point, old_dl, new_dl and pending_dl shall be empty. | ||
| 259 | deleted_dl might not be empty when there was a rename to a | ||
| 260 | nonexisting file. Let's make a check for this (might be removed | ||
| 261 | once the code is stable). */ | ||
| 262 | if (! NILP (old_dl)) | ||
| 263 | report_file_error ("Old list not empty", old_dl); | ||
| 264 | if (! NILP (new_dl)) | ||
| 265 | report_file_error ("New list not empty", new_dl); | ||
| 266 | if (! NILP (pending_dl)) | ||
| 267 | report_file_error ("Pending events list not empty", pending_dl); | ||
| 268 | // if (! NILP (deleted_dl)) | ||
| 269 | // report_file_error ("Deleted events list not empty", deleted_dl); | ||
| 270 | |||
| 271 | /* Replace old directory listing with the new one. */ | ||
| 272 | XSETCDR (Fnthcdr (make_number (3), watch_object), | ||
| 273 | Fcons (new_directory_files, Qnil)); | ||
| 274 | return; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* This is the callback function for arriving input on kqueuefd. It | ||
| 278 | shall create a Lisp event, and put it into the Emacs input queue. */ | ||
| 279 | static void | ||
| 280 | kqueue_callback (int fd, void *data) | ||
| 281 | { | ||
| 282 | for (;;) { | ||
| 283 | struct kevent kev; | ||
| 284 | static const struct timespec nullts = { 0, 0 }; | ||
| 285 | Lisp_Object descriptor, watch_object, file, actions; | ||
| 286 | |||
| 287 | /* Read one event. */ | ||
| 288 | int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts); | ||
| 289 | if (ret < 1) { | ||
| 290 | /* All events read. */ | ||
| 291 | return; | ||
| 292 | } | ||
| 293 | |||
| 294 | /* Determine descriptor and file name. */ | ||
| 295 | descriptor = make_number (kev.ident); | ||
| 296 | watch_object = assq_no_quit (descriptor, watch_list); | ||
| 297 | if (CONSP (watch_object)) | ||
| 298 | file = XCAR (XCDR (watch_object)); | ||
| 299 | else | ||
| 300 | continue; | ||
| 301 | |||
| 302 | /* Determine event actions. */ | ||
| 303 | actions = Qnil; | ||
| 304 | if (kev.fflags & NOTE_DELETE) | ||
| 305 | actions = Fcons (Qdelete, actions); | ||
| 306 | if (kev.fflags & NOTE_WRITE) { | ||
| 307 | /* Check, whether this is a directory event. */ | ||
| 308 | if (NILP (Fnth (make_number (4), watch_object))) | ||
| 309 | actions = Fcons (Qwrite, actions); | ||
| 310 | else | ||
| 311 | kqueue_compare_dir_list (watch_object); | ||
| 312 | } | ||
| 313 | if (kev.fflags & NOTE_EXTEND) | ||
| 314 | actions = Fcons (Qextend, actions); | ||
| 315 | if (kev.fflags & NOTE_ATTRIB) | ||
| 316 | actions = Fcons (Qattrib, actions); | ||
| 317 | if (kev.fflags & NOTE_LINK) | ||
| 318 | actions = Fcons (Qlink, actions); | ||
| 319 | /* It would be useful to know the target of the rename operation. | ||
| 320 | At this point, it is not possible. Happens only when the upper | ||
| 321 | directory is monitored. */ | ||
| 322 | if (kev.fflags & NOTE_RENAME) | ||
| 323 | actions = Fcons (Qrename, actions); | ||
| 324 | |||
| 325 | /* Create the event. */ | ||
| 326 | if (! NILP (actions)) | ||
| 327 | kqueue_generate_event (watch_object, actions, file, Qnil); | ||
| 328 | |||
| 329 | /* Cancel monitor if file or directory is deleted or renamed. */ | ||
| 330 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) | ||
| 331 | Fkqueue_rm_watch (descriptor); | ||
| 332 | } | ||
| 333 | return; | ||
| 334 | } | ||
| 335 | |||
| 336 | DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0, | ||
| 337 | doc: /* Add a watch for filesystem events pertaining to FILE. | ||
| 338 | |||
| 339 | This arranges for filesystem events pertaining to FILE to be reported | ||
| 340 | to Emacs. Use `kqueue-rm-watch' to cancel the watch. | ||
| 341 | |||
| 342 | Returned value is a descriptor for the added watch. If the file cannot be | ||
| 343 | watched for some reason, this function signals a `file-notify-error' error. | ||
| 344 | |||
| 345 | FLAGS is a list of events to be watched for. It can include the | ||
| 346 | following symbols: | ||
| 347 | |||
| 348 | `create' -- FILE was created | ||
| 349 | `delete' -- FILE was deleted | ||
| 350 | `write' -- FILE has changed | ||
| 351 | `extend' -- FILE was extended | ||
| 352 | `attrib' -- a FILE attribute was changed | ||
| 353 | `link' -- a FILE's link count was changed | ||
| 354 | `rename' -- FILE was moved to FILE1 | ||
| 355 | |||
| 356 | When any event happens, Emacs will call the CALLBACK function passing | ||
| 357 | it a single argument EVENT, which is of the form | ||
| 358 | |||
| 359 | (DESCRIPTOR ACTIONS FILE [FILE1]) | ||
| 360 | |||
| 361 | DESCRIPTOR is the same object as the one returned by this function. | ||
| 362 | ACTIONS is a list of events. | ||
| 363 | |||
| 364 | FILE is the name of the file whose event is being reported. FILE1 | ||
| 365 | will be reported only in case of the `rename' event. This is possible | ||
| 366 | only when the upper directory of the renamed file is watched. */) | ||
| 367 | (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) | ||
| 368 | { | ||
| 369 | Lisp_Object watch_object, dir_list; | ||
| 370 | int fd, oflags; | ||
| 371 | u_short fflags = 0; | ||
| 372 | struct kevent kev; | ||
| 373 | |||
| 374 | /* Check parameters. */ | ||
| 375 | CHECK_STRING (file); | ||
| 376 | file = Fdirectory_file_name (Fexpand_file_name (file, Qnil)); | ||
| 377 | if (NILP (Ffile_exists_p (file))) | ||
| 378 | report_file_error ("File does not exist", file); | ||
| 379 | |||
| 380 | CHECK_LIST (flags); | ||
| 381 | |||
| 382 | if (! FUNCTIONP (callback)) | ||
| 383 | wrong_type_argument (Qinvalid_function, callback); | ||
| 384 | |||
| 385 | if (kqueuefd < 0) | ||
| 386 | { | ||
| 387 | /* Create kqueue descriptor. */ | ||
| 388 | kqueuefd = kqueue (); | ||
| 389 | if (kqueuefd < 0) | ||
| 390 | report_file_notify_error ("File watching is not available", Qnil); | ||
| 391 | |||
| 392 | /* Start monitoring for possible I/O. */ | ||
| 393 | add_read_fd (kqueuefd, kqueue_callback, NULL); | ||
| 394 | |||
| 395 | watch_list = Qnil; | ||
| 396 | } | ||
| 397 | |||
| 398 | /* Open file. */ | ||
| 399 | file = ENCODE_FILE (file); | ||
| 400 | oflags = O_NONBLOCK; | ||
| 401 | #if O_EVTONLY | ||
| 402 | oflags |= O_EVTONLY; | ||
| 403 | #else | ||
| 404 | oflags |= O_RDONLY; | ||
| 405 | #endif | ||
| 406 | #if O_SYMLINK | ||
| 407 | oflags |= O_SYMLINK; | ||
| 408 | #else | ||
| 409 | oflags |= O_NOFOLLOW; | ||
| 410 | #endif | ||
| 411 | fd = emacs_open (SSDATA (file), oflags, 0); | ||
| 412 | if (fd == -1) | ||
| 413 | report_file_error ("File cannot be opened", file); | ||
| 414 | |||
| 415 | /* Assemble filter flags */ | ||
| 416 | if (! NILP (Fmember (Qdelete, flags))) fflags |= NOTE_DELETE; | ||
| 417 | if (! NILP (Fmember (Qwrite, flags))) fflags |= NOTE_WRITE; | ||
| 418 | if (! NILP (Fmember (Qextend, flags))) fflags |= NOTE_EXTEND; | ||
| 419 | if (! NILP (Fmember (Qattrib, flags))) fflags |= NOTE_ATTRIB; | ||
| 420 | if (! NILP (Fmember (Qlink, flags))) fflags |= NOTE_LINK; | ||
| 421 | if (! NILP (Fmember (Qrename, flags))) fflags |= NOTE_RENAME; | ||
| 422 | |||
| 423 | /* Register event. */ | ||
| 424 | EV_SET (&kev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, | ||
| 425 | fflags, 0, NULL); | ||
| 426 | |||
| 427 | if (kevent (kqueuefd, &kev, 1, NULL, 0, NULL) < 0) { | ||
| 428 | emacs_close (fd); | ||
| 429 | report_file_error ("Cannot watch file", file); | ||
| 430 | } | ||
| 431 | |||
| 432 | /* Store watch object in watch list. */ | ||
| 433 | Lisp_Object watch_descriptor = make_number (fd); | ||
| 434 | if (NILP (Ffile_directory_p (file))) | ||
| 435 | watch_object = list4 (watch_descriptor, file, flags, callback); | ||
| 436 | else { | ||
| 437 | dir_list = directory_files_internal (file, Qnil, Qnil, Qnil, 1, Qnil); | ||
| 438 | watch_object = list5 (watch_descriptor, file, flags, callback, dir_list); | ||
| 439 | } | ||
| 440 | watch_list = Fcons (watch_object, watch_list); | ||
| 441 | |||
| 442 | return watch_descriptor; | ||
| 443 | } | ||
| 444 | |||
| 445 | DEFUN ("kqueue-rm-watch", Fkqueue_rm_watch, Skqueue_rm_watch, 1, 1, 0, | ||
| 446 | doc: /* Remove an existing WATCH-DESCRIPTOR. | ||
| 447 | |||
| 448 | WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. */) | ||
| 449 | (Lisp_Object watch_descriptor) | ||
| 450 | { | ||
| 451 | Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list); | ||
| 452 | |||
| 453 | if (! CONSP (watch_object)) | ||
| 454 | xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"), | ||
| 455 | watch_descriptor); | ||
| 456 | |||
| 457 | eassert (INTEGERP (watch_descriptor)); | ||
| 458 | int fd = XINT (watch_descriptor); | ||
| 459 | if ( fd >= 0) | ||
| 460 | emacs_close (fd); | ||
| 461 | |||
| 462 | /* Remove watch descriptor from watch list. */ | ||
| 463 | watch_list = Fdelq (watch_object, watch_list); | ||
| 464 | |||
| 465 | if (NILP (watch_list) && (kqueuefd >= 0)) { | ||
| 466 | delete_read_fd (kqueuefd); | ||
| 467 | emacs_close (kqueuefd); | ||
| 468 | kqueuefd = -1; | ||
| 469 | } | ||
| 470 | |||
| 471 | return Qt; | ||
| 472 | } | ||
| 473 | |||
| 474 | DEFUN ("kqueue-valid-p", Fkqueue_valid_p, Skqueue_valid_p, 1, 1, 0, | ||
| 475 | doc: /* "Check a watch specified by its WATCH-DESCRIPTOR. | ||
| 476 | |||
| 477 | WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. | ||
| 478 | |||
| 479 | A watch can become invalid if the file or directory it watches is | ||
| 480 | deleted, or if the watcher thread exits abnormally for any other | ||
| 481 | reason. Removing the watch by calling `kqueue-rm-watch' also makes it | ||
| 482 | invalid. */) | ||
| 483 | (Lisp_Object watch_descriptor) | ||
| 484 | { | ||
| 485 | return NILP (assq_no_quit (watch_descriptor, watch_list)) ? Qnil : Qt; | ||
| 486 | } | ||
| 487 | |||
| 488 | |||
| 489 | void | ||
| 490 | globals_of_kqueue (void) | ||
| 491 | { | ||
| 492 | watch_list = Qnil; | ||
| 493 | } | ||
| 494 | |||
| 495 | void | ||
| 496 | syms_of_kqueue (void) | ||
| 497 | { | ||
| 498 | defsubr (&Skqueue_add_watch); | ||
| 499 | defsubr (&Skqueue_rm_watch); | ||
| 500 | defsubr (&Skqueue_valid_p); | ||
| 501 | |||
| 502 | /* Event types. */ | ||
| 503 | DEFSYM (Qcreate, "create"); | ||
| 504 | DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */ | ||
| 505 | DEFSYM (Qwrite, "write"); /* NOTE_WRITE */ | ||
| 506 | DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */ | ||
| 507 | DEFSYM (Qattrib, "attrib"); /* NOTE_ATTRIB */ | ||
| 508 | DEFSYM (Qlink, "link"); /* NOTE_LINK */ | ||
| 509 | DEFSYM (Qrename, "rename"); /* NOTE_RENAME */ | ||
| 510 | |||
| 511 | staticpro (&watch_list); | ||
| 512 | |||
| 513 | Fprovide (intern_c_string ("kqueue"), Qnil); | ||
| 514 | } | ||
| 515 | |||
| 516 | #endif /* HAVE_KQUEUE */ | ||
| 517 | |||
| 518 | /* PROBLEMS | ||
| 519 | * https://bugs.launchpad.net/ubuntu/+source/libkqueue/+bug/1514837 | ||
| 520 | prevents tests on Ubuntu. */ | ||
diff --git a/src/lisp.h b/src/lisp.h index 3efa492e0e8..426b6c949e9 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4257,17 +4257,23 @@ extern void init_font (void); | |||
| 4257 | extern void syms_of_fontset (void); | 4257 | extern void syms_of_fontset (void); |
| 4258 | #endif | 4258 | #endif |
| 4259 | 4259 | ||
| 4260 | /* Defined in inotify.c */ | ||
| 4261 | #ifdef HAVE_INOTIFY | ||
| 4262 | extern void syms_of_inotify (void); | ||
| 4263 | #endif | ||
| 4264 | |||
| 4265 | /* Defined in kqueue.c */ | ||
| 4266 | #ifdef HAVE_KQUEUE | ||
| 4267 | extern void globals_of_kqueue (void); | ||
| 4268 | extern void syms_of_kqueue (void); | ||
| 4269 | #endif | ||
| 4270 | |||
| 4260 | /* Defined in gfilenotify.c */ | 4271 | /* Defined in gfilenotify.c */ |
| 4261 | #ifdef HAVE_GFILENOTIFY | 4272 | #ifdef HAVE_GFILENOTIFY |
| 4262 | extern void globals_of_gfilenotify (void); | 4273 | extern void globals_of_gfilenotify (void); |
| 4263 | extern void syms_of_gfilenotify (void); | 4274 | extern void syms_of_gfilenotify (void); |
| 4264 | #endif | 4275 | #endif |
| 4265 | 4276 | ||
| 4266 | /* Defined in inotify.c */ | ||
| 4267 | #ifdef HAVE_INOTIFY | ||
| 4268 | extern void syms_of_inotify (void); | ||
| 4269 | #endif | ||
| 4270 | |||
| 4271 | #ifdef HAVE_W32NOTIFY | 4277 | #ifdef HAVE_W32NOTIFY |
| 4272 | /* Defined on w32notify.c. */ | 4278 | /* Defined on w32notify.c. */ |
| 4273 | extern void syms_of_w32notify (void); | 4279 | extern void syms_of_w32notify (void); |