diff options
| author | Michael Albinus | 2015-11-14 11:51:28 +0000 |
|---|---|---|
| committer | Michael Albinus | 2015-11-25 15:07:11 +0100 |
| commit | 41d9bd0c3b19d839b72fdd20e613cb6ab3b1b1f4 (patch) | |
| tree | f511cf454627c1bc1c8175ac12f88f8ac76eb2cf /src | |
| parent | c571fc149a786a5bef7f2b283b912999d76dd313 (diff) | |
| download | emacs-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.c | 149 |
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 | ||
| 4 | This file is part of GNU Emacs. | 4 | This 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. */ |
| 32 | static int kqueuefd = -1; | 33 | static 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]) */ |
| 35 | static Lisp_Object watch_list; | 36 | static Lisp_Object watch_list; |
| 36 | 37 | ||
| 38 | /* Generate a file notification event. */ | ||
| 39 | static void | ||
| 40 | kqueue_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. */ | ||
| 57 | static void | ||
| 58 | kqueue_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. */ |
| 39 | static void | 128 | static 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. | |||
| 109 | FLAGS is a list of events to be watched for. It can include the | 194 | FLAGS is a list of events to be watched for. It can include the |
| 110 | following symbols: | 195 | following 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 | |||
| 128 | will be reported only in case of the `rename' event. */) | 214 | will 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 |