1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2
3 Copyright (C) 2012-2023 Free Software Foundation, Inc.
4
5 Author: Eli Zaretskii <eliz@gnu.org>
6
7 This file is part of GNU Emacs.
8
9 GNU Emacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or (at
12 your option) any later version.
13
14 GNU Emacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
21
22 /* Design overview:
23
24 For each watch request, we launch a separate worker thread. The
25 worker thread runs the watch_worker function, which issues an
26 asynchronous call to ReadDirectoryChangesW, and then calls
27 WaitForSingleObjectEx to wait that an event be signaled
28 to terminate the thread.
29 Waiting with WaitForSingleObjectEx puts the thread in an
30 "alertable" state, so it wakes up when either (a) the call to
31 ReadDirectoryChangesW completes, or (b) the main thread instructs
32 the worker thread to terminate by signaling an event, see below.
33
34 When the ReadDirectoryChangesW call completes, its completion
35 routine watch_completion is automatically called. watch_completion
36 stashes the received file events in a linked list used to
37 communicate them to the main thread (using a critical section, so
38 that several threads could alter the same linked list), posts a
39 special message, WM_EMACS_FILENOTIFY, to the Emacs's message queue,
40 and returns. That causes the WaitForSingleObjectEx function call
41 inside watch_worker to return, but the thread won't terminate until
42 the event telling to do so will be signaled. The completion
43 routine then issues another call to ReadDirectoryChangesW as quickly
44 as possible. (Except when it does not, see below.)
45
46 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
47 message queue gets dispatched to the main Emacs window procedure,
48 which queues it for processing by w32_read_socket. When
49 w32_read_socket sees this message, it accesses the linked list with file
50 notifications (using a critical section), extracts the information,
51 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
52 them into the input event queue to be processed by keyboard.c input
53 machinery (read_char via a call to kbd_buffer_get_event).
54
55 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
56 the main (a.k.a. "Lisp") thread instead, since there are no window
57 procedures in console programs. That message wakes up
58 MsgWaitForMultipleObjects inside sys_select, which then signals to
59 its caller that some keyboard input is available. This causes
60 w32_console_read_socket to be called, which accesses the linked list
61 with file notifications and stuffs them into the input event queue
62 for keyboard.c to process.
63
64 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
65 kbd_buffer_get_event, it is converted to a Lispy event that can be
66 bound to a command. The default binding is file-notify-handle-event,
67 defined on subr.el.
68
69 Routines w32_read_socket or w32_console_read_socket process notifications
70 sets as long as some are available.
71
72 When the watch is removed by a call to w32notify-rm-watch, the main
73 thread requests that the worker thread terminates by signaling the
74 appropriate event and queuing an APC for the worker thread. The
75 APC specifies the watch_end function to be called. watch_end calls
76 CancelIo on the outstanding ReadDirectoryChangesW call. When
77 watch_end returns, the watch_completion function is called one last
78 time with the ERROR_OPERATION_ABORTED status, which causes it to
79 clean up and set a flag telling watch_worker to exit without
80 issuing another ReadDirectoryChangesW call. Since watch_worker is
81 the thread procedure of the worker thread, exiting it causes the
82 thread to exit. The main thread waits for some time for the worker
83 thread to exit, and if it doesn't, terminates it forcibly. */
84
85 #define DEFER_MS_W32_H
86 #include <config.h>
87
88 #include <stddef.h>
89 #include <errno.h>
90
91 /* Include CRT headers *before* ms-w32.h. */
92 #include <ms-w32.h>
93
94 #include <windows.h>
95
96 #include "lisp.h"
97 #include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
98 #include "w32common.h" /* for OS version data */
99 #include "w32.h" /* for w32_strerror */
100 #include "coding.h"
101 #include "keyboard.h"
102 #include "frame.h" /* needed by termhooks.h */
103 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
104
105 #define DIRWATCH_BUFFER_SIZE 16384
106 #define DIRWATCH_SIGNATURE 0x01233210
107
108 struct notification {
109 BYTE *buf; /* buffer for ReadDirectoryChangesW */
110 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
111 BOOL subtree; /* whether to watch subdirectories */
112 DWORD filter; /* bit mask for events to watch */
113 char *watchee; /* the file we are interested in, UTF-8 encoded */
114 HANDLE dir; /* handle to the watched directory */
115 HANDLE thr; /* handle to the thread that watches */
116 HANDLE terminate; /* event signaling the thread to terminate */
117 unsigned signature;
118 };
119
120 /* Used for communicating notifications to the main thread. */
121 struct notifications_set *notifications_set_head;
122
123 static Lisp_Object watch_list;
124
125 /* Signal to the main thread that we have file notifications for it to
126 process. */
127 static void
128 send_notifications (struct notifications_set *ns)
129 {
130 struct frame *f = SELECTED_FRAME ();
131
132 /* We add the current notification set to the linked list. Use the
133 critical section to make sure only one thread will access the
134 linked list. */
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 /* If PostMessage fails, the message queue is full. If that
143 happens, the last thing they will worry about is file
144 notifications. So we effectively discard the notification in
145 that case. */
146 if (FRAME_TERMCAP_P (f))
147 /* We send the message to the main (a.k.a. "Lisp") thread, where
148 it will wake up MsgWaitForMultipleObjects inside sys_select,
149 causing it to report that there's some keyboard input
150 available. This will in turn cause w32_console_read_socket to
151 be called, which will pick up the file notifications. */
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 /* When we are running in batch mode, there's no one to send a
157 message, so we just signal the data is available and hope
158 sys_select will be called soon and will read the data. */
159 #if 0
160 else if (FRAME_INITIAL_P (f) && noninteractive)
161 ;
162 #endif
163 }
164
165 /* An APC routine to cancel outstanding directory watch. Invoked by
166 the main thread via QueueUserAPC. This is needed because only the
167 thread that issued the ReadDirectoryChangesW call can call CancelIo
168 to cancel that. (CancelIoEx is only available since Vista, so we
169 cannot use it on XP.) */
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 /* A completion routine (a.k.a. "APC function") for handling events
182 read by ReadDirectoryChangesW. Called by the OS when the thread
183 which issued the asynchronous ReadDirectoryChangesW call is in the
184 "alertable state", i.e. waiting inside SleepEx call. */
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 /* Who knows what happened? Perhaps the OVERLAPPED structure was
196 freed by someone already? In any case, we cannot do anything
197 with this request, so just punt and skip it. FIXME: should we
198 raise the 'terminate' flag in this case? */
199 if (!io_info)
200 {
201 DebPrint(("watch_completion: io_info is null.\n"));
202 return;
203 }
204
205 /* We have a pointer to our dirwatch structure conveniently stashed
206 away in the hEvent member of the OVERLAPPED struct. According to
207 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
208 of the OVERLAPPED structure is not used by the system, so you can
209 use it yourself." */
210 dirwatch = (struct notification *)io_info->hEvent;
211
212 if (status == ERROR_OPERATION_ABORTED)
213 {
214 /* We've been called because the main thread told us to issue
215 CancelIo on the directory we watch, and watch_end did so.
216 We must exit, without issuing another call to
217 ReadDirectoryChangesW. */
218 return;
219 }
220
221 /* We allocate a new set of notifications to be linked to the linked
222 list of notifications set. This will be processed by Emacs event
223 loop in the main thread. We need to duplicate the notifications
224 buffer, but not the dirwatch structure. */
225
226 /* Implementation note: In general, allocating memory in non-main
227 threads is a no-no in Emacs. We certainly cannot call xmalloc
228 and friends, because it can longjmp when allocation fails, which
229 will crash Emacs because the jmp_buf is set up to a location on
230 the main thread's stack. However, we can call 'malloc' directly,
231 since that is redirected to HeapAlloc that uses our private heap,
232 see w32heap.c, and that is thread-safe. */
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 /* Calling ReadDirectoryChangesW quickly to watch again for new
254 notifications. */
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 /* If this call fails, it means that the directory is not
262 watchable any more. We need to terminate the worker thread.
263 Still, we will wait until the current notifications have been
264 sent to the main thread. */
265 terminate = TRUE;
266 }
267
268 if (ns)
269 send_notifications(ns);
270
271 /* If we were asked to terminate the thread, then fire the event. */
272 if (terminate)
273 SetEvent(dirwatch->terminate);
274 }
275
276 /* Worker routine for the watch thread. */
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 /* We cannot remove the dirwatch object from watch_list,
295 because we are in a separate thread. For the same
296 reason, we also cannot free memory consumed by the
297 buffers allocated for the dirwatch object. So we close
298 the directory handle, but do not free the object itself
299 or its buffers. We also don't touch the signature. This
300 way, remove_watch can still identify the object, remove
301 it, and free its memory. */
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 /* The thread is about to terminate, so we clean up the dir handle. */
313 CloseHandle (dirwatch->dir);
314 dirwatch->dir = NULL;
315
316 return 0;
317 }
318 /* Launch a thread to watch changes to FILE in a directory open on
319 handle HDIR. */
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 /* Stash a pointer to dirwatch structure for use by the completion
329 routine. According to MSDN documentation of ReadDirectoryChangesW:
330 "The hEvent member of the OVERLAPPED structure is not used by the
331 system, so you can use it yourself." */
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 /* See w32proc.c where it calls CreateThread for the story behind
342 the 2nd and 5th argument in the call to CreateThread. */
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 /* Called from the main thread to start watching FILE in PARENT_DIR,
358 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
359 PARENT_DIR as well. Value is a pointer to 'struct notification'
360 used by the thread that watches the changes. */
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 /* Do not follow symlinks, so that the caller could watch symlink
371 files. */
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 /* FILE_SHARE_DELETE doesn't preclude other
389 processes from deleting files inside
390 parent_dir. */
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 /* Stop watching a directory specified by a pointer to its dirwatch object. */
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 /* Only the thread that issued the outstanding I/O call can call
434 CancelIo on it. (CancelIoEx is available only since Vista.)
435 So we need to queue an APC for the worker thread telling it
436 to terminate. */
437 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
438 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
439
440 /* We also signal the thread that it can terminate. */
441 SetEvent(dirwatch->terminate);
442
443 /* Wait for the thread to exit. FIXME: is there a better method
444 that is not overly complex? */
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 /* Clean up. */
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: /* Add a watch for filesystem events pertaining to FILE.
516
517 This arranges for filesystem events pertaining to FILE to be reported
518 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
519
520 Value is a descriptor for the added watch. If the file cannot be
521 watched for some reason, this function signals a `file-error' error.
522
523 FILTER is a list of conditions for reporting an event. It can include
524 the following symbols:
525
526 `file-name' -- report file creation, deletion, or renaming
527 `directory-name' -- report directory creation, deletion, or renaming
528 `attributes' -- report changes in attributes
529 `size' -- report changes in file-size
530 `last-write-time' -- report changes in last-write time
531 `last-access-time' -- report changes in last-access time
532 `creation-time' -- report changes in creation time
533 `security-desc' -- report changes in security descriptor
534
535 If FILE is a directory, and FILTER includes `subtree', then all the
536 subdirectories will also be watched and changes in them reported.
537
538 When any event happens that satisfies the conditions specified by
539 FILTER, Emacs will call the CALLBACK function passing it a single
540 argument EVENT, which is of the form
541
542 (DESCRIPTOR ACTION FILE)
543
544 DESCRIPTOR is the same object as the one returned by this function.
545 ACTION is the description of the event. It could be any one of the
546 following:
547
548 `added' -- FILE was added
549 `removed' -- FILE was deleted
550 `modified' -- FILE's contents or its attributes were modified
551 `renamed-from' -- a file was renamed whose old name was FILE
552 `renamed-to' -- a file was renamed and its new name is FILE
553
554 FILE is the name of the file whose event is being reported.
555
556 Note that some networked filesystems, such as Samba-mounted Unix
557 volumes, might not send notifications about file changes. In these
558 cases, this function will return a valid descriptor, but notifications
559 will never come in. Volumes shared from remote Windows machines do
560 generate notifications correctly, though. */)
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 /* The underlying features are available only since XP. */
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 /* filenotify.el always passes us a directory, either the parent
582 directory of a file to be watched, or the directory to be
583 watched. */
584 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
585 if (NILP (Ffile_directory_p (file)))
586 {
587 /* This should only happen if we are called directly, not via
588 filenotify.el. If BASEFN is empty, the argument was the root
589 directory on its drive. */
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 /* Store watch object in watch list. */
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: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
639
640 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
641 (Lisp_Object watch_descriptor)
642 {
643 Lisp_Object watch_object;
644 struct notification *dirwatch;
645 int status = -1;
646
647 /* Remove the watch object from watch list. Do this before freeing
648 the object, do that even if we fail to free it, watch_list is
649 kept free of junk. */
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 /* This is called from the input queue handling code, inside a
672 critical section, so we cannot possibly quit if watch_list is not
673 in the right condition. */
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: /* Check a watch specified by its WATCH-DESCRIPTOR for validity.
679
680 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.
681
682 A watch can become invalid if the directory it watches is deleted, or if
683 the watcher thread exits abnormally for any other reason. Removing the
684 watch by calling `w32notify-rm-watch' also makes it invalid. */)
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 }