This source file includes following definitions.
- send_notifications
- watch_end
- watch_completion
- watch_worker
- start_watching
- add_watch
- remove_watch
- filter_list_to_flags
- DEFUN
- w32_get_watch_object
- DEFUN
- globals_of_w32notify
- syms_of_w32notify
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85 #define DEFER_MS_W32_H
86 #include <config.h>
87
88 #include <stddef.h>
89 #include <errno.h>
90
91
92 #include <ms-w32.h>
93
94 #include <windows.h>
95
96 #include "lisp.h"
97 #include "w32term.h"
98 #include "w32common.h"
99 #include "w32.h"
100 #include "coding.h"
101 #include "keyboard.h"
102 #include "frame.h"
103 #include "termhooks.h"
104
105 #define DIRWATCH_BUFFER_SIZE 16384
106 #define DIRWATCH_SIGNATURE 0x01233210
107
108 struct notification {
109 BYTE *buf;
110 OVERLAPPED *io_info;
111 BOOL subtree;
112 DWORD filter;
113 char *watchee;
114 HANDLE dir;
115 HANDLE thr;
116 HANDLE terminate;
117 unsigned signature;
118 };
119
120
121 struct notifications_set *notifications_set_head;
122
123 static Lisp_Object watch_list;
124
125
126
127 static void
128 send_notifications (struct notifications_set *ns)
129 {
130 struct frame *f = SELECTED_FRAME ();
131
132
133
134
135 enter_crit ();
136 ns->next = notifications_set_head;
137 ns->prev = notifications_set_head->prev;
138 ns->prev->next = ns;
139 notifications_set_head->prev = ns;
140 leave_crit();
141
142
143
144
145
146 if (FRAME_TERMCAP_P (f))
147
148
149
150
151
152 PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0);
153 else if (FRAME_W32_P (f))
154 PostMessage (FRAME_W32_WINDOW (f),
155 WM_EMACS_FILENOTIFY, 0, 0);
156
157
158
159 #if 0
160 else if (FRAME_INITIAL_P (f) && noninteractive)
161 ;
162 #endif
163 }
164
165
166
167
168
169
170 VOID CALLBACK watch_end (ULONG_PTR);
171
172 VOID CALLBACK
173 watch_end (ULONG_PTR arg)
174 {
175 HANDLE hdir = (HANDLE)arg;
176
177 if (hdir && hdir != INVALID_HANDLE_VALUE)
178 CancelIo (hdir);
179 }
180
181
182
183
184
185 VOID CALLBACK watch_completion (DWORD, DWORD, OVERLAPPED *);
186
187 VOID CALLBACK
188 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
189 {
190 struct notification *dirwatch;
191 DWORD _bytes;
192 struct notifications_set *ns = NULL;
193 BOOL terminate = FALSE;
194
195
196
197
198
199 if (!io_info)
200 {
201 DebPrint(("watch_completion: io_info is null.\n"));
202 return;
203 }
204
205
206
207
208
209
210 dirwatch = (struct notification *)io_info->hEvent;
211
212 if (status == ERROR_OPERATION_ABORTED)
213 {
214
215
216
217
218 return;
219 }
220
221
222
223
224
225
226
227
228
229
230
231
232
233 ns = malloc (sizeof(struct notifications_set));
234 if (ns)
235 {
236 memset (ns, 0, sizeof(struct notifications_set));
237 ns->notifications = malloc (bytes_ret);
238 if (ns->notifications)
239 {
240 memcpy (ns->notifications, dirwatch->buf, bytes_ret);
241 ns->size = bytes_ret;
242 ns->desc = dirwatch;
243 }
244 else
245 {
246 free (ns);
247 ns = NULL;
248 }
249 }
250 if (ns == NULL)
251 DebPrint(("Out of memory. Notifications lost."));
252
253
254
255 if (!ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
256 DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
257 dirwatch->filter, &_bytes, dirwatch->io_info,
258 watch_completion))
259 {
260 DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
261
262
263
264
265 terminate = TRUE;
266 }
267
268 if (ns)
269 send_notifications(ns);
270
271
272 if (terminate)
273 SetEvent(dirwatch->terminate);
274 }
275
276
277 static DWORD WINAPI
278 watch_worker (LPVOID arg)
279 {
280 struct notification *dirwatch = (struct notification *)arg;
281 BOOL bErr;
282 DWORD _bytes = 0;
283 DWORD status;
284
285 if (dirwatch->dir)
286 {
287 bErr = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
288 DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
289 dirwatch->filter, &_bytes,
290 dirwatch->io_info, watch_completion);
291 if (!bErr)
292 {
293 DebPrint (("ReadDirectoryChangesW: %lu\n", GetLastError ()));
294
295
296
297
298
299
300
301
302 CloseHandle (dirwatch->dir);
303 dirwatch->dir = NULL;
304 return 1;
305 }
306 }
307
308 do {
309 status = WaitForSingleObjectEx(dirwatch->terminate, INFINITE, TRUE);
310 } while (status == WAIT_IO_COMPLETION);
311
312
313 CloseHandle (dirwatch->dir);
314 dirwatch->dir = NULL;
315
316 return 0;
317 }
318
319
320 static struct notification *
321 start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
322 {
323 struct notification *dirwatch = xzalloc (sizeof (struct notification));
324
325 dirwatch->signature = DIRWATCH_SIGNATURE;
326 dirwatch->buf = xmalloc (DIRWATCH_BUFFER_SIZE);
327 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
328
329
330
331
332 dirwatch->io_info->hEvent = dirwatch;
333 dirwatch->subtree = subdirs;
334 dirwatch->filter = flags;
335 dirwatch->watchee = xstrdup (file);
336
337 dirwatch->terminate = CreateEvent(NULL, FALSE, FALSE, NULL);
338
339 dirwatch->dir = hdir;
340
341
342
343 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
344 0x00010000, NULL);
345
346 if (!dirwatch->thr)
347 {
348 CloseHandle(dirwatch->terminate);
349 xfree (dirwatch->buf);
350 xfree (dirwatch->io_info);
351 xfree (dirwatch->watchee);
352 xfree (dirwatch);
353 }
354 return dirwatch;
355 }
356
357
358
359
360
361 static struct notification *
362 add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
363 {
364 HANDLE hdir;
365 struct notification *dirwatch = NULL;
366
367 if (!file)
368 return NULL;
369
370
371
372 DWORD crflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
373 if (symlinks_supported (parent_dir))
374 crflags |= FILE_FLAG_OPEN_REPARSE_POINT;
375
376 if (w32_unicode_filenames)
377 {
378 wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
379
380 filename_to_utf16 (parent_dir, dir_w);
381 if (*file)
382 filename_to_utf16 (file, file_w);
383 else
384 file_w[0] = 0;
385
386 hdir = CreateFileW (dir_w,
387 FILE_LIST_DIRECTORY,
388
389
390
391 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
392 NULL, OPEN_EXISTING, crflags,
393 NULL);
394 }
395 else
396 {
397 char dir_a[MAX_PATH], file_a[MAX_PATH];
398
399 filename_to_ansi (parent_dir, dir_a);
400 if (*file)
401 filename_to_ansi (file, file_a);
402 else
403 file_a[0] = '\0';
404
405 hdir = CreateFileA (dir_a,
406 FILE_LIST_DIRECTORY,
407 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
408 NULL, OPEN_EXISTING, crflags,
409 NULL);
410 }
411 if (hdir == INVALID_HANDLE_VALUE)
412 return NULL;
413
414 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
415 {
416 CloseHandle (hdir);
417 dirwatch->dir = NULL;
418 }
419
420 return dirwatch;
421 }
422
423
424 static int
425 remove_watch (struct notification *dirwatch)
426 {
427 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
428 {
429 int i;
430 BOOL status;
431 DWORD exit_code = 0, err = 0;
432
433
434
435
436
437 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
438 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
439
440
441 SetEvent(dirwatch->terminate);
442
443
444
445 for (i = 0; i < 50; i++)
446 {
447 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
448 && exit_code == STILL_ACTIVE))
449 break;
450 Sleep (10);
451 }
452
453 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
454 || exit_code == STILL_ACTIVE)
455 {
456 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
457 {
458 DebPrint(("Forcing thread termination.\n"));
459 TerminateThread (dirwatch->thr, 0);
460 if (dirwatch->dir)
461 CloseHandle (dirwatch->dir);
462 }
463 }
464
465
466 if (dirwatch->thr)
467 {
468 CloseHandle (dirwatch->thr);
469 dirwatch->thr = NULL;
470 }
471 CloseHandle(dirwatch->terminate);
472 xfree (dirwatch->buf);
473 xfree (dirwatch->io_info);
474 xfree (dirwatch->watchee);
475 xfree (dirwatch);
476 return 0;
477 }
478 else
479 {
480 DebPrint (("Unknown dirwatch object!\n"));
481 return -1;
482 }
483 }
484
485 static DWORD
486 filter_list_to_flags (Lisp_Object filter_list)
487 {
488 DWORD flags = 0;
489
490 if (NILP (filter_list))
491 return flags;
492
493 if (!NILP (Fmember (Qfile_name, filter_list)))
494 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
495 if (!NILP (Fmember (Qdirectory_name, filter_list)))
496 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
497 if (!NILP (Fmember (Qattributes, filter_list)))
498 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
499 if (!NILP (Fmember (Qsize, filter_list)))
500 flags |= FILE_NOTIFY_CHANGE_SIZE;
501 if (!NILP (Fmember (Qlast_write_time, filter_list)))
502 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
503 if (!NILP (Fmember (Qlast_access_time, filter_list)))
504 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
505 if (!NILP (Fmember (Qcreation_time, filter_list)))
506 flags |= FILE_NOTIFY_CHANGE_CREATION;
507 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
508 flags |= FILE_NOTIFY_CHANGE_SECURITY;
509
510 return flags;
511 }
512
513 DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
514 Sw32notify_add_watch, 3, 3, 0,
515 doc:
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560 )
561 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
562 {
563 Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
564 DWORD flags;
565 BOOL subdirs = FALSE;
566 struct notification *dirwatch = NULL;
567 Lisp_Object lisp_errstr;
568 char *errstr;
569
570 CHECK_LIST (filter);
571
572
573 if (os_subtype == OS_SUBTYPE_9X
574 || (w32_major_version == 5 && w32_minor_version < 1))
575 {
576 errno = ENOSYS;
577 report_file_notify_error ("Watching filesystem events is not supported",
578 Qnil);
579 }
580
581
582
583
584 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
585 if (NILP (Ffile_directory_p (file)))
586 {
587
588
589
590 dirfn = ENCODE_FILE (Ffile_name_directory (file));
591 basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
592 if (*SDATA (basefn) == '\0')
593 subdirs = TRUE;
594 }
595 else
596 {
597 dirfn = ENCODE_FILE (file);
598 basefn = Qnil;
599 }
600
601 if (!NILP (Fmember (Qsubtree, filter)))
602 subdirs = TRUE;
603
604 flags = filter_list_to_flags (filter);
605
606 dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
607 subdirs, flags);
608 if (!dirwatch)
609 {
610 DWORD err = GetLastError ();
611
612 errno = EINVAL;
613 if (err)
614 {
615 errstr = w32_strerror (err);
616 if (!NILP (Vlocale_coding_system))
617 lisp_errstr
618 = code_convert_string_norecord (build_unibyte_string (errstr),
619 Vlocale_coding_system, 0);
620 else
621 lisp_errstr = build_string (errstr);
622 report_file_notify_error ("Cannot watch file",
623 Fcons (lisp_errstr, Fcons (file, Qnil)));
624 }
625 else
626 report_file_notify_error ("Cannot watch file", Fcons (file, Qnil));
627 }
628
629 watch_descriptor = make_mint_ptr (dirwatch);
630 watch_object = Fcons (watch_descriptor, callback);
631 watch_list = Fcons (watch_object, watch_list);
632
633 return watch_descriptor;
634 }
635
636 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
637 Sw32notify_rm_watch, 1, 1, 0,
638 doc:
639
640 )
641 (Lisp_Object watch_descriptor)
642 {
643 Lisp_Object watch_object;
644 struct notification *dirwatch;
645 int status = -1;
646
647
648
649
650 watch_object = Fassoc (watch_descriptor, watch_list, Qnil);
651 if (!NILP (watch_object))
652 {
653 watch_list = Fdelete (watch_object, watch_list);
654 dirwatch = (struct notification *)xmint_pointer (watch_descriptor);
655 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
656 status = remove_watch (dirwatch);
657 }
658
659 if (status == -1)
660 report_file_notify_error ("Invalid watch descriptor",
661 Fcons (watch_descriptor, Qnil));
662
663 return Qnil;
664 }
665
666 Lisp_Object
667 w32_get_watch_object (void *desc)
668 {
669 Lisp_Object descriptor = make_mint_ptr (desc);
670
671
672
673
674 return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
675 }
676
677 DEFUN ("w32notify-valid-p", Fw32notify_valid_p, Sw32notify_valid_p, 1, 1, 0,
678 doc:
679
680
681
682
683
684 )
685 (Lisp_Object watch_descriptor)
686 {
687 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list, Qnil);
688
689 if (!NILP (watch_object))
690 {
691 struct notification *dirwatch =
692 (struct notification *)xmint_pointer (watch_descriptor);
693 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))
694 && dirwatch->dir != NULL)
695 return Qt;
696 }
697
698 return Qnil;
699 }
700
701 void
702 globals_of_w32notify (void)
703 {
704 watch_list = Qnil;
705 }
706
707 void
708 syms_of_w32notify (void)
709 {
710 DEFSYM (Qfile_name, "file-name");
711 DEFSYM (Qdirectory_name, "directory-name");
712 DEFSYM (Qattributes, "attributes");
713 DEFSYM (Qlast_write_time, "last-write-time");
714 DEFSYM (Qlast_access_time, "last-access-time");
715 DEFSYM (Qcreation_time, "creation-time");
716 DEFSYM (Qsecurity_desc, "security-desc");
717 DEFSYM (Qsubtree, "subtree");
718
719 defsubr (&Sw32notify_add_watch);
720 defsubr (&Sw32notify_rm_watch);
721 defsubr (&Sw32notify_valid_p);
722
723 staticpro (&watch_list);
724
725 Fprovide (intern_c_string ("w32notify"), Qnil);
726 }