aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMichael Albinus2015-11-14 11:51:28 +0000
committerMichael Albinus2015-11-25 15:07:11 +0100
commit41d9bd0c3b19d839b72fdd20e613cb6ab3b1b1f4 (patch)
treef511cf454627c1bc1c8175ac12f88f8ac76eb2cf /src
parentc571fc149a786a5bef7f2b283b912999d76dd313 (diff)
downloademacs-41d9bd0c3b19d839b72fdd20e613cb6ab3b1b1f4.tar.gz
emacs-41d9bd0c3b19d839b72fdd20e613cb6ab3b1b1f4.zip
Implement directory events
* lisp/filenotify.el (file-notify-handle-event) (file-notify-callback): Remove traces. * src/kqueue.c: Include <sys/time.h>. (kqueue_generate_event, kqueue_compare_dir_list): New functions. (kqueue_callback): Use them. Call kevent() with a zero timeout. (Fkqueue_add_watch): Adapt docstring. Support directory events. Compute initial directory listing. Close file descriptor in case of errors. (syms_of_kqueue): Declare Qcreate.
Diffstat (limited to 'src')
-rw-r--r--src/kqueue.c149
1 files changed, 119 insertions, 30 deletions
diff --git a/src/kqueue.c b/src/kqueue.c
index d2f3d37e19c..0425a142a98 100644
--- a/src/kqueue.c
+++ b/src/kqueue.c
@@ -1,5 +1,5 @@
1/* Filesystem notifications support with glib API. 1/* Filesystem notifications support with kqueue API.
2 Copyright (C) 2013-2015 Free Software Foundation, Inc. 2 Copyright (C) 2015 Free Software Foundation, Inc.
3 3
4This file is part of GNU Emacs. 4This file is part of GNU Emacs.
5 5
@@ -22,6 +22,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
22#include <stdio.h> 22#include <stdio.h>
23#include <sys/types.h> 23#include <sys/types.h>
24#include <sys/event.h> 24#include <sys/event.h>
25#include <sys/time.h>
25#include <sys/file.h> 26#include <sys/file.h>
26#include "lisp.h" 27#include "lisp.h"
27#include "keyboard.h" 28#include "keyboard.h"
@@ -31,9 +32,97 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
31/* File handle for kqueue. */ 32/* File handle for kqueue. */
32static int kqueuefd = -1; 33static int kqueuefd = -1;
33 34
34/* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */ 35/* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]) */
35static Lisp_Object watch_list; 36static Lisp_Object watch_list;
36 37
38/* Generate a file notification event. */
39static void
40kqueue_generate_event
41(Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object callback)
42{
43 struct input_event event;
44 EVENT_INIT (event);
45 event.kind = FILE_NOTIFY_EVENT;
46 event.frame_or_window = Qnil;
47 event.arg = list2 (Fcons (ident, Fcons (actions, Fcons (file, Qnil))),
48 callback);
49
50 /* Store it into the input event queue. */
51 kbd_buffer_store_event (&event);
52}
53
54/* This compares two directory listings in case of a `write' event for
55 a directory. The old directory listing is stored in watch_object,
56 it will be replaced by a new directory listing at the end. */
57static void
58kqueue_compare_dir_list (Lisp_Object watch_object)
59{
60 Lisp_Object dir, callback, old_dl, new_dl, dl, actions;
61
62 dir = XCAR (XCDR (watch_object));
63 callback = XCAR (XCDR (XCDR (XCDR (watch_object))));
64 old_dl = XCAR (XCDR (XCDR (XCDR (XCDR (watch_object)))));
65 new_dl = directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil);
66
67 for (dl = old_dl; ! NILP (dl); dl = XCDR (dl)) {
68 Lisp_Object old_entry, new_entry;
69 old_entry = XCAR (dl);
70 new_entry = Fassoc (XCAR (old_entry), new_dl);
71
72 /* We ignore "." and "..". */
73 if ((strcmp (".", SSDATA (XCAR (old_entry))) == 0) ||
74 (strcmp ("..", SSDATA (XCAR (old_entry))) == 0))
75 continue;
76
77 /* A file has disappeared. */
78 if (NILP (new_entry))
79 kqueue_generate_event
80 (XCAR (watch_object), Fcons (Qdelete, Qnil),
81 XCAR (old_entry), callback);
82
83 else {
84 /* A file has changed. We compare last modification time. */
85 if (NILP
86 (Fequal
87 (XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (old_entry))))))),
88 XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (new_entry))))))))))
89 kqueue_generate_event
90 (XCAR (watch_object), Fcons (Qwrite, Qnil),
91 XCAR (old_entry), callback);
92
93 /* A file attribute has changed. We compare last status change time. */
94 if (NILP
95 (Fequal
96 (XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (old_entry)))))))),
97 XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (new_entry)))))))))))
98 kqueue_generate_event
99 (XCAR (watch_object), Fcons (Qattrib, Qnil),
100 XCAR (old_entry), callback);
101 }
102 }
103
104 for (dl = new_dl; ! NILP (dl); dl = XCDR (dl)) {
105 Lisp_Object old_entry, new_entry;
106 new_entry = XCAR (dl);
107 old_entry = Fassoc (XCAR (new_entry), old_dl);
108
109 /* We ignore "." and "..". */
110 if ((strcmp (".", SSDATA (XCAR (new_entry))) == 0) ||
111 (strcmp ("..", SSDATA (XCAR (new_entry))) == 0))
112 continue;
113
114 /* A new file has appeared. */
115 if (NILP (old_entry))
116 kqueue_generate_event
117 (XCAR (watch_object), Fcons (Qcreate, Qnil),
118 XCAR (new_entry), callback);
119 }
120
121 /* Replace directory listing with the new one. */
122 XSETCDR (XCDR (XCDR (XCDR (watch_object))), Fcons (new_dl, Qnil));
123 return;
124}
125
37/* This is the callback function for arriving input on kqueuefd. It 126/* This is the callback function for arriving input on kqueuefd. It
38 shall create a Lisp event, and put it into Emacs input queue. */ 127 shall create a Lisp event, and put it into Emacs input queue. */
39static void 128static void
@@ -41,11 +130,11 @@ kqueue_callback (int fd, void *data)
41{ 130{
42 for (;;) { 131 for (;;) {
43 struct kevent kev; 132 struct kevent kev;
44 struct input_event event; 133 static const struct timespec nullts = { 0, 0 };
45 Lisp_Object monitor_object, watch_object, file, callback, actions; 134 Lisp_Object monitor_object, watch_object, file, callback, dirp, actions;
46 135
47 /* Read one event. */ 136 /* Read one event. */
48 int ret = kevent (kqueuefd, NULL, 0, &kev, 1, NULL); 137 int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts);
49 if (ret < 1) { 138 if (ret < 1) {
50 /* All events read. */ 139 /* All events read. */
51 return; 140 return;
@@ -58,6 +147,7 @@ kqueue_callback (int fd, void *data)
58 if (CONSP (watch_object)) { 147 if (CONSP (watch_object)) {
59 file = XCAR (XCDR (watch_object)); 148 file = XCAR (XCDR (watch_object));
60 callback = XCAR (XCDR (XCDR (XCDR (watch_object)))); 149 callback = XCAR (XCDR (XCDR (XCDR (watch_object))));
150 dirp = XCDR (XCDR (XCDR (XCDR (watch_object))));
61 } 151 }
62 else 152 else
63 continue; 153 continue;
@@ -66,8 +156,12 @@ kqueue_callback (int fd, void *data)
66 actions = Qnil; 156 actions = Qnil;
67 if (kev.fflags & NOTE_DELETE) 157 if (kev.fflags & NOTE_DELETE)
68 actions = Fcons (Qdelete, actions); 158 actions = Fcons (Qdelete, actions);
69 if (kev.fflags & NOTE_WRITE) 159 if (kev.fflags & NOTE_WRITE) {
70 actions = Fcons (Qwrite, actions); 160 if (NILP (dirp))
161 actions = Fcons (Qwrite, actions);
162 else
163 kqueue_compare_dir_list (watch_object);
164 }
71 if (kev.fflags & NOTE_EXTEND) 165 if (kev.fflags & NOTE_EXTEND)
72 actions = Fcons (Qextend, actions); 166 actions = Fcons (Qextend, actions);
73 if (kev.fflags & NOTE_ATTRIB) 167 if (kev.fflags & NOTE_ATTRIB)
@@ -77,18 +171,9 @@ kqueue_callback (int fd, void *data)
77 if (kev.fflags & NOTE_RENAME) 171 if (kev.fflags & NOTE_RENAME)
78 actions = Fcons (Qrename, actions); 172 actions = Fcons (Qrename, actions);
79 173
80 if (! NILP (actions)) { 174 /* Construct an event. */
81 /* Construct an event. */ 175 if (! NILP (actions))
82 EVENT_INIT (event); 176 kqueue_generate_event (monitor_object, actions, file, callback);
83 event.kind = FILE_NOTIFY_EVENT;
84 event.frame_or_window = Qnil;
85 event.arg = list2 (Fcons (monitor_object,
86 Fcons (actions, Fcons (file, Qnil))),
87 callback);
88
89 /* Store it into the input event queue. */
90 kbd_buffer_store_event (&event);
91 }
92 177
93 /* Cancel monitor if file or directory is deleted. */ 178 /* Cancel monitor if file or directory is deleted. */
94 if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) 179 if (kev.fflags & (NOTE_DELETE | NOTE_RENAME))
@@ -109,6 +194,7 @@ watched for some reason, this function signals a `file-notify-error' error.
109FLAGS is a list of events to be watched for. It can include the 194FLAGS is a list of events to be watched for. It can include the
110following symbols: 195following symbols:
111 196
197 `create' -- FILE was created
112 `delete' -- FILE was deleted 198 `delete' -- FILE was deleted
113 `write' -- FILE has changed 199 `write' -- FILE has changed
114 `extend' -- FILE was extended 200 `extend' -- FILE was extended
@@ -128,7 +214,7 @@ FILE is the name of the file whose event is being reported. FILE1
128will be reported only in case of the `rename' event. */) 214will be reported only in case of the `rename' event. */)
129 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) 215 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
130{ 216{
131 Lisp_Object watch_object; 217 Lisp_Object watch_object, dir_list;
132 int fd; 218 int fd;
133 u_short fflags = 0; 219 u_short fflags = 0;
134 struct kevent ev; 220 struct kevent ev;
@@ -139,10 +225,6 @@ will be reported only in case of the `rename' event. */)
139 if (NILP (Ffile_exists_p (file))) 225 if (NILP (Ffile_exists_p (file)))
140 report_file_error ("File does not exist", file); 226 report_file_error ("File does not exist", file);
141 227
142 /* TODO: Directories shall be supported as well. */
143 if (! NILP (Ffile_directory_p (file)))
144 report_file_error ("Directory watching is not supported (yet)", file);
145
146 CHECK_LIST (flags); 228 CHECK_LIST (flags);
147 229
148 if (! FUNCTIONP (callback)) 230 if (! FUNCTIONP (callback))
@@ -156,14 +238,14 @@ will be reported only in case of the `rename' event. */)
156 report_file_notify_error ("File watching is not available", Qnil); 238 report_file_notify_error ("File watching is not available", Qnil);
157 239
158 /* Start monitoring for possible I/O. */ 240 /* Start monitoring for possible I/O. */
159 add_read_fd (kqueuefd, kqueue_callback, NULL); //data); 241 add_read_fd (kqueuefd, kqueue_callback, NULL);
160 242
161 watch_list = Qnil; 243 watch_list = Qnil;
162 } 244 }
163 245
164 /* Open file. */ 246 /* Open file. */
165 file = ENCODE_FILE (file); 247 file = ENCODE_FILE (file);
166 fd = emacs_open (SSDATA (file), O_NONBLOCK | O_RDONLY, 0); 248 fd = emacs_open (SSDATA (file), O_RDONLY, 0);
167 if (fd == -1) 249 if (fd == -1)
168 report_file_error ("File cannot be opened", file); 250 report_file_error ("File cannot be opened", file);
169 251
@@ -179,12 +261,19 @@ will be reported only in case of the `rename' event. */)
179 EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, 261 EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
180 fflags, 0, NULL); 262 fflags, 0, NULL);
181 263
182 if (kevent (kqueuefd, &ev, 1, NULL, 0, NULL) < 0) 264 if (kevent (kqueuefd, &ev, 1, NULL, 0, NULL) < 0) {
265 emacs_close (fd);
183 report_file_error ("Cannot watch file", file); 266 report_file_error ("Cannot watch file", file);
267 }
184 268
185 /* Store watch object in watch list. */ 269 /* Store watch object in watch list. */
186 Lisp_Object watch_descriptor = make_number (fd); 270 Lisp_Object watch_descriptor = make_number (fd);
187 watch_object = list4 (watch_descriptor, file, flags, callback); 271 if (NILP (Ffile_directory_p (file)))
272 watch_object = list4 (watch_descriptor, file, flags, callback);
273 else {
274 dir_list = directory_files_internal (file, Qnil, Qnil, Qnil, 1, Qnil);
275 watch_object = list5 (watch_descriptor, file, flags, callback, dir_list);
276 }
188 watch_list = Fcons (watch_object, watch_list); 277 watch_list = Fcons (watch_object, watch_list);
189 278
190 return watch_descriptor; 279 return watch_descriptor;
@@ -248,6 +337,7 @@ syms_of_kqueue (void)
248 defsubr (&Skqueue_valid_p); 337 defsubr (&Skqueue_valid_p);
249 338
250 /* Event types. */ 339 /* Event types. */
340 DEFSYM (Qcreate, "create");
251 DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */ 341 DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */
252 DEFSYM (Qwrite, "write"); /* NOTE_WRITE */ 342 DEFSYM (Qwrite, "write"); /* NOTE_WRITE */
253 DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */ 343 DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */
@@ -263,7 +353,6 @@ syms_of_kqueue (void)
263#endif /* HAVE_KQUEUE */ 353#endif /* HAVE_KQUEUE */
264 354
265/* TODO 355/* TODO
266 * Implement watching directories.
267 * Add FILE1 in case of `rename'. */ 356 * Add FILE1 in case of `rename'. */
268 357
269/* PROBLEMS 358/* PROBLEMS