This source file includes following definitions.
- kqueue_directory_listing
- kqueue_generate_event
- kqueue_compare_dir_list
- kqueue_callback
- DEFUN
- DEFUN
- globals_of_kqueue
- syms_of_kqueue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <config.h>
21
22 #include <sys/types.h>
23 #include <sys/event.h>
24 #include <sys/time.h>
25 #include <fcntl.h>
26 #include "lisp.h"
27 #include "keyboard.h"
28 #include "process.h"
29
30 #ifdef HAVE_SYS_RESOURCE_H
31 #include <sys/resource.h>
32 #endif
33
34
35
36 static int kqueuefd = -1;
37
38
39 static Lisp_Object watch_list;
40
41
42
43 static Lisp_Object
44 kqueue_directory_listing (Lisp_Object directory_files)
45 {
46 Lisp_Object dl, result = Qnil;
47
48 for (dl = directory_files; ! NILP (dl); dl = XCDR (dl)) {
49
50 if ((strcmp (".", SSDATA (XCAR (XCAR (dl)))) == 0) ||
51 (strcmp ("..", SSDATA (XCAR (XCAR (dl)))) == 0))
52 continue;
53
54 result = Fcons
55 (list5 (
56 Fnth (make_fixnum (11), XCAR (dl)),
57
58 XCAR (XCAR (dl)),
59
60 Fnth (make_fixnum (6), XCAR (dl)),
61
62 Fnth (make_fixnum (7), XCAR (dl)),
63
64 Fnth (make_fixnum (8), XCAR (dl))),
65 result);
66 }
67 return result;
68 }
69
70
71 static void
72 kqueue_generate_event (Lisp_Object watch_object, Lisp_Object actions,
73 Lisp_Object file, Lisp_Object file1)
74 {
75 Lisp_Object flags, action, entry;
76 struct input_event event;
77
78
79 flags = Fnth (make_fixnum (2), watch_object);
80 action = actions;
81 do {
82 if (NILP (action))
83 break;
84 entry = XCAR (action);
85 if (NILP (Fmember (entry, flags))) {
86 action = XCDR (action);
87 actions = Fdelq (entry, actions);
88 } else
89 action = XCDR (action);
90 } while (1);
91
92
93 if (! NILP (actions)) {
94 EVENT_INIT (event);
95 event.kind = FILE_NOTIFY_EVENT;
96 event.frame_or_window = Qnil;
97 event.arg = list2 (Fcons (XCAR (watch_object),
98 Fcons (actions,
99 NILP (file1)
100 ? list1 (file)
101 : list2 (file, file1))),
102 Fnth (make_fixnum (3), watch_object));
103 kbd_buffer_store_event (&event);
104 }
105 }
106
107
108
109
110
111
112 static void
113 kqueue_compare_dir_list (Lisp_Object watch_object)
114 {
115 Lisp_Object dir, pending_dl, deleted_dl;
116 Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl;
117
118 dir = XCAR (XCDR (watch_object));
119 pending_dl = Qnil;
120 deleted_dl = Qnil;
121
122 old_directory_files = Fnth (make_fixnum (4), watch_object);
123 old_dl = kqueue_directory_listing (old_directory_files);
124
125
126 if (NILP (Ffile_directory_p (dir))) {
127 kqueue_generate_event (watch_object, Fcons (Qdelete, Qnil), dir, Qnil);
128 return;
129 }
130 new_directory_files =
131 directory_files_internal (dir, Qnil, Qnil, Qnil, true, Qnil, Qnil);
132 new_dl = kqueue_directory_listing (new_directory_files);
133
134
135 dl = old_dl;
136 while (1) {
137 Lisp_Object old_entry, new_entry, dl1;
138 if (NILP (dl))
139 break;
140
141
142 old_entry = XCAR (dl);
143 new_entry = assq_no_quit (XCAR (old_entry), new_dl);
144 if (! NILP (Fequal (old_entry, new_entry))) {
145
146 new_dl = Fdelq (new_entry, new_dl);
147 goto the_end;
148 }
149
150
151 if (! NILP (new_entry)) {
152
153 if (strcmp (SSDATA (XCAR (XCDR (old_entry))),
154 SSDATA (XCAR (XCDR (new_entry)))) == 0) {
155
156 if (NILP (Fequal (Fnth (make_fixnum (2), old_entry),
157 Fnth (make_fixnum (2), new_entry))))
158 kqueue_generate_event
159 (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (old_entry)), Qnil);
160
161
162 if (NILP (Fequal (Fnth (make_fixnum (3), old_entry),
163 Fnth (make_fixnum (3), new_entry))))
164 kqueue_generate_event
165 (watch_object, Fcons (Qattrib, Qnil),
166 XCAR (XCDR (old_entry)), Qnil);
167
168 } else {
169
170 kqueue_generate_event
171 (watch_object, Fcons (Qrename, Qnil),
172 XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry)));
173 deleted_dl = Fcons (new_entry, deleted_dl);
174 }
175 new_dl = Fdelq (new_entry, new_dl);
176 goto the_end;
177 }
178
179
180
181 for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) {
182 new_entry = XCAR (dl1);
183 if (strcmp (SSDATA (XCAR (XCDR (old_entry))),
184 SSDATA (XCAR (XCDR (new_entry)))) == 0) {
185 pending_dl = Fcons (new_entry, pending_dl);
186 new_dl = Fdelq (new_entry, new_dl);
187 goto the_end;
188 }
189 }
190
191
192 new_entry = assq_no_quit (XCAR (old_entry), pending_dl);
193
194 if (NILP (new_entry)) {
195
196 for (dl1 = deleted_dl; ! NILP (dl1); dl1 = XCDR (dl1)) {
197 new_entry = XCAR (dl1);
198 if (strcmp (SSDATA (XCAR (XCDR (old_entry))),
199 SSDATA (XCAR (XCDR (new_entry)))) == 0) {
200 deleted_dl = Fdelq (new_entry, deleted_dl);
201 goto the_end;
202 }
203 }
204
205 kqueue_generate_event
206 (watch_object, Fcons (Qdelete, Qnil), XCAR (XCDR (old_entry)), Qnil);
207
208 } else {
209
210 kqueue_generate_event
211 (watch_object, Fcons (Qrename, Qnil),
212 XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry)));
213 pending_dl = Fdelq (new_entry, pending_dl);
214 }
215
216 the_end:
217 dl = XCDR (dl);
218 old_dl = Fdelq (old_entry, old_dl);
219 }
220
221
222 dl = new_dl;
223 while (1) {
224 Lisp_Object entry;
225 if (NILP (dl))
226 break;
227
228
229 entry = XCAR (dl);
230 kqueue_generate_event
231 (watch_object, Fcons (Qcreate, Qnil), XCAR (XCDR (entry)), Qnil);
232
233
234 Lisp_Object size = Fnth (make_fixnum (4), entry);
235 if (FLOATP (size) || (XFIXNUM (size) > 0))
236 kqueue_generate_event
237 (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil);
238
239 dl = XCDR (dl);
240 new_dl = Fdelq (entry, new_dl);
241 }
242
243
244 dl = pending_dl;
245 while (1) {
246 Lisp_Object entry;
247 if (NILP (dl))
248 break;
249
250
251 entry = XCAR (dl);
252 kqueue_generate_event
253 (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil);
254
255 dl = XCDR (dl);
256 pending_dl = Fdelq (entry, pending_dl);
257 }
258
259
260
261
262
263 if (! NILP (old_dl))
264 report_file_error ("Old list not empty", old_dl);
265 if (! NILP (new_dl))
266 report_file_error ("New list not empty", new_dl);
267 if (! NILP (pending_dl))
268 report_file_error ("Pending events list not empty", pending_dl);
269
270
271 XSETCDR (Fnthcdr (make_fixnum (3), watch_object),
272 Fcons (new_directory_files, Qnil));
273 return;
274 }
275
276
277
278 static void
279 kqueue_callback (int fd, void *data)
280 {
281 for (;;) {
282 struct kevent kev;
283 static const struct timespec nullts = { 0, 0 };
284 Lisp_Object descriptor, watch_object, file, actions;
285
286
287 int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts);
288 if (ret < 1) {
289
290 return;
291 }
292
293
294 descriptor = make_fixnum (kev.ident);
295 watch_object = assq_no_quit (descriptor, watch_list);
296 if (CONSP (watch_object))
297 file = XCAR (XCDR (watch_object));
298 else
299 continue;
300
301
302 actions = Qnil;
303 if (kev.fflags & NOTE_DELETE)
304 actions = Fcons (Qdelete, actions);
305 if (kev.fflags & NOTE_WRITE) {
306
307 if (NILP (Fnth (make_fixnum (4), watch_object)))
308 actions = Fcons (Qwrite, actions);
309 else
310 kqueue_compare_dir_list (watch_object);
311 }
312 if (kev.fflags & NOTE_EXTEND)
313 actions = Fcons (Qextend, actions);
314 if (kev.fflags & NOTE_ATTRIB)
315 actions = Fcons (Qattrib, actions);
316 if (kev.fflags & NOTE_LINK)
317 actions = Fcons (Qlink, actions);
318
319
320
321 if (kev.fflags & NOTE_RENAME)
322 actions = Fcons (Qrename, actions);
323
324
325 if (! NILP (actions))
326 kqueue_generate_event (watch_object, actions, file, Qnil);
327
328
329 if (kev.fflags & (NOTE_DELETE | NOTE_RENAME))
330 Fkqueue_rm_watch (descriptor);
331 }
332 return;
333 }
334
335 DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0,
336 doc:
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365 )
366 (Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
367 {
368 Lisp_Object watch_object, dir_list;
369 int maxfd, fd, oflags;
370 u_short fflags = 0;
371 struct kevent kev;
372 #ifdef HAVE_GETRLIMIT
373 struct rlimit rlim;
374 #endif
375
376
377 CHECK_STRING (file);
378 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
379 if (NILP (Ffile_exists_p (file)))
380 report_file_error ("File does not exist", file);
381
382 CHECK_LIST (flags);
383
384 if (! FUNCTIONP (callback))
385 wrong_type_argument (Qinvalid_function, callback);
386
387
388 #ifdef HAVE_GETRLIMIT
389 if (! getrlimit (RLIMIT_NOFILE, &rlim))
390 maxfd = rlim.rlim_cur;
391 else
392 #endif
393 maxfd = 256;
394
395
396 ptrdiff_t watch_list_len = list_length (watch_list);
397 if (maxfd - 50 < watch_list_len)
398 xsignal2
399 (Qfile_notify_error,
400 build_string ("File watching not possible, no file descriptor left"),
401 make_fixnum (watch_list_len));
402
403 if (kqueuefd < 0)
404 {
405
406 kqueuefd = kqueue ();
407 if (kqueuefd < 0)
408 report_file_notify_error ("File watching is not available", Qnil);
409
410
411 add_read_fd (kqueuefd, kqueue_callback, NULL);
412
413 watch_list = Qnil;
414 }
415
416
417 Lisp_Object encoded_file = ENCODE_FILE (file);
418 oflags = O_NONBLOCK;
419 #if O_EVTONLY
420 oflags |= O_EVTONLY;
421 #else
422 oflags |= O_RDONLY;
423 #endif
424 #if O_SYMLINK
425 oflags |= O_SYMLINK;
426 #else
427 oflags |= O_NOFOLLOW;
428 #endif
429 fd = emacs_open (SSDATA (encoded_file), oflags, 0);
430 if (fd == -1)
431 report_file_error ("File cannot be opened", file);
432
433
434 if (! NILP (Fmember (Qdelete, flags))) fflags |= NOTE_DELETE;
435 if (! NILP (Fmember (Qwrite, flags))) fflags |= NOTE_WRITE;
436 if (! NILP (Fmember (Qextend, flags))) fflags |= NOTE_EXTEND;
437 if (! NILP (Fmember (Qattrib, flags))) fflags |= NOTE_ATTRIB;
438 if (! NILP (Fmember (Qlink, flags))) fflags |= NOTE_LINK;
439 if (! NILP (Fmember (Qrename, flags))) fflags |= NOTE_RENAME;
440
441
442 EV_SET (&kev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR,
443 fflags, 0, NULL);
444
445 if (kevent (kqueuefd, &kev, 1, NULL, 0, NULL) < 0) {
446 emacs_close (fd);
447 report_file_error ("Cannot watch file", file);
448 }
449
450
451 Lisp_Object watch_descriptor = make_fixnum (fd);
452 if (NILP (Ffile_directory_p (file)))
453 watch_object = list4 (watch_descriptor, file, flags, callback);
454 else {
455 dir_list = directory_files_internal (file, Qnil, Qnil, Qnil, true, Qnil,
456 Qnil);
457 watch_object = list5 (watch_descriptor, file, flags, callback, dir_list);
458 }
459 watch_list = Fcons (watch_object, watch_list);
460
461 return watch_descriptor;
462 }
463
464 DEFUN ("kqueue-rm-watch", Fkqueue_rm_watch, Skqueue_rm_watch, 1, 1, 0,
465 doc:
466
467 )
468 (Lisp_Object watch_descriptor)
469 {
470 Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
471
472 if (! CONSP (watch_object))
473 xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
474 watch_descriptor);
475
476 eassert (FIXNUMP (watch_descriptor));
477 int fd = XFIXNUM (watch_descriptor);
478 if ( fd >= 0)
479 emacs_close (fd);
480
481
482 watch_list = Fdelq (watch_object, watch_list);
483
484 if (NILP (watch_list) && (kqueuefd >= 0)) {
485 delete_read_fd (kqueuefd);
486 emacs_close (kqueuefd);
487 kqueuefd = -1;
488 }
489
490 return Qt;
491 }
492
493 DEFUN ("kqueue-valid-p", Fkqueue_valid_p, Skqueue_valid_p, 1, 1, 0,
494 doc:
495
496
497
498
499
500
501 )
502 (Lisp_Object watch_descriptor)
503 {
504 return NILP (assq_no_quit (watch_descriptor, watch_list)) ? Qnil : Qt;
505 }
506
507
508 void
509 globals_of_kqueue (void)
510 {
511 watch_list = Qnil;
512 }
513
514 void
515 syms_of_kqueue (void)
516 {
517 defsubr (&Skqueue_add_watch);
518 defsubr (&Skqueue_rm_watch);
519 defsubr (&Skqueue_valid_p);
520
521
522 DEFSYM (Qcreate, "create");
523 DEFSYM (Qdelete, "delete");
524 DEFSYM (Qwrite, "write");
525 DEFSYM (Qextend, "extend");
526 DEFSYM (Qattrib, "attrib");
527 DEFSYM (Qlink, "link");
528 DEFSYM (Qrename, "rename");
529
530 staticpro (&watch_list);
531
532 Fprovide (intern_c_string ("kqueue"), Qnil);
533 }
534
535
536
537