diff options
| author | Michael Albinus | 2015-11-17 15:46:29 +0000 |
|---|---|---|
| committer | Michael Albinus | 2015-11-25 15:07:11 +0100 |
| commit | 13f3508443e4f5c48d40e4dbb11eaf875a5b2042 (patch) | |
| tree | 81749fbeeffa5e826bb68b27dd58d0e50f9c7d20 | |
| parent | 99aa85535aebd96eff294250caef9ab2de2334b7 (diff) | |
| download | emacs-13f3508443e4f5c48d40e4dbb11eaf875a5b2042.tar.gz emacs-13f3508443e4f5c48d40e4dbb11eaf875a5b2042.zip | |
Code cleanup of kqueue.c
* src/kqueue.c (kqueue_directory_listing): Skip "." and "..".
(kqueue_compare_dir_list): Do not loop when calling
directory_files_internal. Remove checks for "." and "..", this is
done in kqueue_directory_listing now.
(Fkqueue_add_watch): Check for proper emacs_open flags.
| -rw-r--r-- | src/kqueue.c | 109 |
1 files changed, 57 insertions, 52 deletions
diff --git a/src/kqueue.c b/src/kqueue.c index dfd91397370..5caef67b92a 100644 --- a/src/kqueue.c +++ b/src/kqueue.c | |||
| @@ -35,13 +35,19 @@ static int kqueuefd = -1; | |||
| 35 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]). */ | 35 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]). */ |
| 36 | static Lisp_Object watch_list; | 36 | static Lisp_Object watch_list; |
| 37 | 37 | ||
| 38 | /* Generate a temporary list from the directory_files_internal output. | 38 | /* Generate a list from the directory_files_internal output. |
| 39 | Items are (INODE FILE-NAME LAST-MOD LAST-STATUS-MOD SIZE). */ | 39 | Items are (INODE FILE-NAME LAST-MOD LAST-STATUS-MOD SIZE). */ |
| 40 | Lisp_Object | 40 | Lisp_Object |
| 41 | kqueue_directory_listing (Lisp_Object directory_files) | 41 | kqueue_directory_listing (Lisp_Object directory_files) |
| 42 | { | 42 | { |
| 43 | Lisp_Object dl, result = Qnil; | 43 | Lisp_Object dl, result = Qnil; |
| 44 | |||
| 44 | for (dl = directory_files; ! NILP (dl); dl = XCDR (dl)) { | 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 | |||
| 45 | result = Fcons | 51 | result = Fcons |
| 46 | (list5 (/* inode. */ | 52 | (list5 (/* inode. */ |
| 47 | Fnth (make_number (11), XCAR (dl)), | 53 | Fnth (make_number (11), XCAR (dl)), |
| @@ -61,7 +67,8 @@ kqueue_directory_listing (Lisp_Object directory_files) | |||
| 61 | /* Generate a file notification event. */ | 67 | /* Generate a file notification event. */ |
| 62 | static void | 68 | static void |
| 63 | kqueue_generate_event | 69 | kqueue_generate_event |
| 64 | (Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object file1, Lisp_Object callback) | 70 | (Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object file1, |
| 71 | Lisp_Object callback) | ||
| 65 | { | 72 | { |
| 66 | struct input_event event; | 73 | struct input_event event; |
| 67 | EVENT_INIT (event); | 74 | EVENT_INIT (event); |
| @@ -78,14 +85,15 @@ kqueue_generate_event | |||
| 78 | } | 85 | } |
| 79 | 86 | ||
| 80 | /* This compares two directory listings in case of a `write' event for | 87 | /* This compares two directory listings in case of a `write' event for |
| 81 | a directory. The old directory listing is stored in watch_object, | 88 | a directory. Generate resulting file notification events. The old |
| 82 | it will be replaced by a new directory listing at the end of this | 89 | directory listing is retrieved from watch_object, it will be |
| 90 | replaced by the new directory listing at the end of this | ||
| 83 | function. */ | 91 | function. */ |
| 84 | static void | 92 | static void |
| 85 | kqueue_compare_dir_list | 93 | kqueue_compare_dir_list |
| 86 | (Lisp_Object watch_object) | 94 | (Lisp_Object watch_object) |
| 87 | { | 95 | { |
| 88 | Lisp_Object dir, callback, actions; | 96 | Lisp_Object dir, callback; |
| 89 | Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; | 97 | Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; |
| 90 | 98 | ||
| 91 | dir = XCAR (XCDR (watch_object)); | 99 | dir = XCAR (XCDR (watch_object)); |
| @@ -94,37 +102,28 @@ kqueue_compare_dir_list | |||
| 94 | old_directory_files = Fnth (make_number (4), watch_object); | 102 | old_directory_files = Fnth (make_number (4), watch_object); |
| 95 | old_dl = kqueue_directory_listing (old_directory_files); | 103 | old_dl = kqueue_directory_listing (old_directory_files); |
| 96 | 104 | ||
| 97 | /* Sometimes, the directory write event is triggered when the change | 105 | /* When the directory is not accessible anymore, it has been deleted. */ |
| 98 | is not visible yet in the directory itself. So we must wait a | ||
| 99 | little bit. */ | ||
| 100 | if (NILP (Ffile_directory_p (dir))) { | 106 | if (NILP (Ffile_directory_p (dir))) { |
| 101 | kqueue_generate_event | 107 | kqueue_generate_event |
| 102 | (XCAR (watch_object), Fcons (Qdelete, Qnil), dir, Qnil, callback); | 108 | (XCAR (watch_object), Fcons (Qdelete, Qnil), dir, Qnil, callback); |
| 103 | return; | 109 | return; |
| 104 | } | 110 | } |
| 105 | do { | 111 | new_directory_files = |
| 106 | new_directory_files = | 112 | directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); |
| 107 | directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); | ||
| 108 | } while (! NILP (Fequal (old_directory_files, new_directory_files))); | ||
| 109 | new_dl = kqueue_directory_listing (new_directory_files); | 113 | new_dl = kqueue_directory_listing (new_directory_files); |
| 110 | 114 | ||
| 111 | /* Parse through the old list. */ | 115 | /* Parse through the old list. */ |
| 112 | dl = old_dl; | 116 | dl = old_dl; |
| 113 | while (1) { | 117 | while (1) { |
| 114 | Lisp_Object old_entry, new_entry; | 118 | Lisp_Object old_entry, new_entry, dl1; |
| 115 | if (NILP (dl)) | 119 | if (NILP (dl)) |
| 116 | break; | 120 | break; |
| 117 | 121 | ||
| 118 | /* We ignore "." and "..". */ | ||
| 119 | old_entry = XCAR (dl); | ||
| 120 | if ((strcmp (".", SSDATA (XCAR (XCDR (old_entry)))) == 0) || | ||
| 121 | (strcmp ("..", SSDATA (XCAR (XCDR (old_entry)))) == 0)) | ||
| 122 | goto the_end; | ||
| 123 | |||
| 124 | /* Search for an entry with the same inode. */ | 122 | /* Search for an entry with the same inode. */ |
| 123 | old_entry = XCAR (dl); | ||
| 125 | new_entry = Fassoc (XCAR (old_entry), new_dl); | 124 | new_entry = Fassoc (XCAR (old_entry), new_dl); |
| 126 | if (! NILP (Fequal (old_entry, new_entry))) { | 125 | if (! NILP (Fequal (old_entry, new_entry))) { |
| 127 | /* Both entries are identical. Nothing happens. */ | 126 | /* Both entries are identical. Nothing to do. */ |
| 128 | new_dl = Fdelq (new_entry, new_dl); | 127 | new_dl = Fdelq (new_entry, new_dl); |
| 129 | goto the_end; | 128 | goto the_end; |
| 130 | } | 129 | } |
| @@ -158,9 +157,8 @@ kqueue_compare_dir_list | |||
| 158 | goto the_end; | 157 | goto the_end; |
| 159 | } | 158 | } |
| 160 | 159 | ||
| 161 | /* Search, whether there is a file with the same name (with | 160 | /* Search, whether there is a file with the same name but another |
| 162 | another inode). */ | 161 | inode. */ |
| 163 | Lisp_Object dl1; | ||
| 164 | for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { | 162 | for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { |
| 165 | new_entry = XCAR (dl1); | 163 | new_entry = XCAR (dl1); |
| 166 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | 164 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), |
| @@ -173,7 +171,7 @@ kqueue_compare_dir_list | |||
| 173 | } | 171 | } |
| 174 | } | 172 | } |
| 175 | 173 | ||
| 176 | /* A file has been deleted. */ | 174 | /* The file has been deleted. */ |
| 177 | kqueue_generate_event | 175 | kqueue_generate_event |
| 178 | (XCAR (watch_object), Fcons (Qdelete, Qnil), | 176 | (XCAR (watch_object), Fcons (Qdelete, Qnil), |
| 179 | XCAR (XCDR (old_entry)), Qnil, callback); | 177 | XCAR (XCDR (old_entry)), Qnil, callback); |
| @@ -183,23 +181,15 @@ kqueue_compare_dir_list | |||
| 183 | old_dl = Fdelq (old_entry, old_dl); | 181 | old_dl = Fdelq (old_entry, old_dl); |
| 184 | } | 182 | } |
| 185 | 183 | ||
| 186 | /* Parse through the shortened new list. */ | 184 | /* Parse through the resulting new list. */ |
| 187 | dl = new_dl; | 185 | dl = new_dl; |
| 188 | while (1) { | 186 | while (1) { |
| 189 | Lisp_Object new_entry; | 187 | Lisp_Object new_entry; |
| 190 | if (NILP (dl)) | 188 | if (NILP (dl)) |
| 191 | break; | 189 | break; |
| 192 | 190 | ||
| 193 | /* We ignore "." and "..". */ | ||
| 194 | new_entry = XCAR (dl); | ||
| 195 | if ((strcmp (".", SSDATA (XCAR (XCDR (new_entry)))) == 0) || | ||
| 196 | (strcmp ("..", SSDATA (XCAR (XCDR (new_entry)))) == 0)) { | ||
| 197 | dl = XCDR (dl); | ||
| 198 | new_dl = Fdelq (new_entry, new_dl); | ||
| 199 | continue; | ||
| 200 | } | ||
| 201 | |||
| 202 | /* A new file has appeared. */ | 191 | /* A new file has appeared. */ |
| 192 | new_entry = XCAR (dl); | ||
| 203 | kqueue_generate_event | 193 | kqueue_generate_event |
| 204 | (XCAR (watch_object), Fcons (Qcreate, Qnil), | 194 | (XCAR (watch_object), Fcons (Qcreate, Qnil), |
| 205 | XCAR (XCDR (new_entry)), Qnil, callback); | 195 | XCAR (XCDR (new_entry)), Qnil, callback); |
| @@ -222,21 +212,21 @@ kqueue_compare_dir_list | |||
| 222 | if (! NILP (new_dl)) | 212 | if (! NILP (new_dl)) |
| 223 | report_file_error ("New list not empty", new_dl); | 213 | report_file_error ("New list not empty", new_dl); |
| 224 | 214 | ||
| 225 | /* Replace directory listing with the new one. */ | 215 | /* Replace old directory listing with the new one. */ |
| 226 | XSETCDR (Fnthcdr (make_number (3), watch_object), | 216 | XSETCDR (Fnthcdr (make_number (3), watch_object), |
| 227 | Fcons (new_directory_files, Qnil)); | 217 | Fcons (new_directory_files, Qnil)); |
| 228 | return; | 218 | return; |
| 229 | } | 219 | } |
| 230 | 220 | ||
| 231 | /* This is the callback function for arriving input on kqueuefd. It | 221 | /* This is the callback function for arriving input on kqueuefd. It |
| 232 | shall create a Lisp event, and put it into Emacs input queue. */ | 222 | shall create a Lisp event, and put it into the Emacs input queue. */ |
| 233 | static void | 223 | static void |
| 234 | kqueue_callback (int fd, void *data) | 224 | kqueue_callback (int fd, void *data) |
| 235 | { | 225 | { |
| 236 | for (;;) { | 226 | for (;;) { |
| 237 | struct kevent kev; | 227 | struct kevent kev; |
| 238 | static const struct timespec nullts = { 0, 0 }; | 228 | static const struct timespec nullts = { 0, 0 }; |
| 239 | Lisp_Object monitor_object, watch_object, file, callback, dirp, actions; | 229 | Lisp_Object descriptor, watch_object, file, callback, actions; |
| 240 | 230 | ||
| 241 | /* Read one event. */ | 231 | /* Read one event. */ |
| 242 | int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts); | 232 | int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts); |
| @@ -245,14 +235,13 @@ kqueue_callback (int fd, void *data) | |||
| 245 | return; | 235 | return; |
| 246 | } | 236 | } |
| 247 | 237 | ||
| 248 | /* Determine file name and callback function. */ | 238 | /* Determine descriptor, file name and callback function. */ |
| 249 | monitor_object = make_number (kev.ident); | 239 | descriptor = make_number (kev.ident); |
| 250 | watch_object = assq_no_quit (monitor_object, watch_list); | 240 | watch_object = assq_no_quit (descriptor, watch_list); |
| 251 | 241 | ||
| 252 | if (CONSP (watch_object)) { | 242 | if (CONSP (watch_object)) { |
| 253 | file = XCAR (XCDR (watch_object)); | 243 | file = XCAR (XCDR (watch_object)); |
| 254 | callback = Fnth (make_number (3), watch_object); | 244 | callback = Fnth (make_number (3), watch_object); |
| 255 | dirp = Fnth (make_number (4), watch_object); | ||
| 256 | } | 245 | } |
| 257 | else | 246 | else |
| 258 | continue; | 247 | continue; |
| @@ -262,7 +251,8 @@ kqueue_callback (int fd, void *data) | |||
| 262 | if (kev.fflags & NOTE_DELETE) | 251 | if (kev.fflags & NOTE_DELETE) |
| 263 | actions = Fcons (Qdelete, actions); | 252 | actions = Fcons (Qdelete, actions); |
| 264 | if (kev.fflags & NOTE_WRITE) { | 253 | if (kev.fflags & NOTE_WRITE) { |
| 265 | if (NILP (dirp)) | 254 | /* Check, whether this is a directory event. */ |
| 255 | if (NILP (Fnth (make_number (4), watch_object))) | ||
| 266 | actions = Fcons (Qwrite, actions); | 256 | actions = Fcons (Qwrite, actions); |
| 267 | else | 257 | else |
| 268 | kqueue_compare_dir_list (watch_object); | 258 | kqueue_compare_dir_list (watch_object); |
| @@ -273,16 +263,19 @@ kqueue_callback (int fd, void *data) | |||
| 273 | actions = Fcons (Qattrib, actions); | 263 | actions = Fcons (Qattrib, actions); |
| 274 | if (kev.fflags & NOTE_LINK) | 264 | if (kev.fflags & NOTE_LINK) |
| 275 | actions = Fcons (Qlink, actions); | 265 | actions = Fcons (Qlink, actions); |
| 266 | /* It would be useful to know the target of the rename operation. | ||
| 267 | At this point, it is not possible. Happens only when the upper | ||
| 268 | directory is monitored. */ | ||
| 276 | if (kev.fflags & NOTE_RENAME) | 269 | if (kev.fflags & NOTE_RENAME) |
| 277 | actions = Fcons (Qrename, actions); | 270 | actions = Fcons (Qrename, actions); |
| 278 | 271 | ||
| 279 | /* Construct an event. */ | 272 | /* Create the event. */ |
| 280 | if (! NILP (actions)) | 273 | if (! NILP (actions)) |
| 281 | kqueue_generate_event (monitor_object, actions, file, Qnil, callback); | 274 | kqueue_generate_event (descriptor, actions, file, Qnil, callback); |
| 282 | 275 | ||
| 283 | /* Cancel monitor if file or directory is deleted. */ | 276 | /* Cancel monitor if file or directory is deleted or renamed. */ |
| 284 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) | 277 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) |
| 285 | Fkqueue_rm_watch (monitor_object); | 278 | Fkqueue_rm_watch (descriptor); |
| 286 | } | 279 | } |
| 287 | return; | 280 | return; |
| 288 | } | 281 | } |
| @@ -316,13 +309,14 @@ DESCRIPTOR is the same object as the one returned by this function. | |||
| 316 | ACTIONS is a list of events. | 309 | ACTIONS is a list of events. |
| 317 | 310 | ||
| 318 | FILE is the name of the file whose event is being reported. FILE1 | 311 | FILE is the name of the file whose event is being reported. FILE1 |
| 319 | will be reported only in case of the `rename' event. */) | 312 | will be reported only in case of the `rename' event. This is possible |
| 313 | only when the upper directory of the renamed file is watched. */) | ||
| 320 | (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) | 314 | (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) |
| 321 | { | 315 | { |
| 322 | Lisp_Object watch_object, dir_list; | 316 | Lisp_Object watch_object, dir_list; |
| 323 | int fd; | 317 | int fd, oflags; |
| 324 | u_short fflags = 0; | 318 | u_short fflags = 0; |
| 325 | struct kevent ev; | 319 | struct kevent kev; |
| 326 | 320 | ||
| 327 | /* Check parameters. */ | 321 | /* Check parameters. */ |
| 328 | CHECK_STRING (file); | 322 | CHECK_STRING (file); |
| @@ -350,7 +344,18 @@ will be reported only in case of the `rename' event. */) | |||
| 350 | 344 | ||
| 351 | /* Open file. */ | 345 | /* Open file. */ |
| 352 | file = ENCODE_FILE (file); | 346 | file = ENCODE_FILE (file); |
| 353 | fd = emacs_open (SSDATA (file), O_RDONLY, 0); | 347 | oflags = O_NONBLOCK; |
| 348 | #if O_EVTONLY | ||
| 349 | oflags |= O_EVTONLY; | ||
| 350 | #else | ||
| 351 | oflags |= O_RDONLY; | ||
| 352 | #endif | ||
| 353 | #if O_SYMLINK | ||
| 354 | oflags |= O_SYMLINK; | ||
| 355 | #else | ||
| 356 | oflags |= O_NOFOLLOW; | ||
| 357 | #endif | ||
| 358 | fd = emacs_open (SSDATA (file), oflags, 0); | ||
| 354 | if (fd == -1) | 359 | if (fd == -1) |
| 355 | report_file_error ("File cannot be opened", file); | 360 | report_file_error ("File cannot be opened", file); |
| 356 | 361 | ||
| @@ -363,10 +368,10 @@ will be reported only in case of the `rename' event. */) | |||
| 363 | if (! NILP (Fmember (Qrename, flags))) fflags |= NOTE_RENAME; | 368 | if (! NILP (Fmember (Qrename, flags))) fflags |= NOTE_RENAME; |
| 364 | 369 | ||
| 365 | /* Register event. */ | 370 | /* Register event. */ |
| 366 | EV_SET (&ev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, | 371 | EV_SET (&kev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, |
| 367 | fflags, 0, NULL); | 372 | fflags, 0, NULL); |
| 368 | 373 | ||
| 369 | if (kevent (kqueuefd, &ev, 1, NULL, 0, NULL) < 0) { | 374 | if (kevent (kqueuefd, &kev, 1, NULL, 0, NULL) < 0) { |
| 370 | emacs_close (fd); | 375 | emacs_close (fd); |
| 371 | report_file_error ("Cannot watch file", file); | 376 | report_file_error ("Cannot watch file", file); |
| 372 | } | 377 | } |