Tested and fixed conversion of a descriptor (pointer) to event code.
[bpt/emacs.git] / src / w32notify.c
CommitLineData
6f011d81
EZ
1/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
c5c91b84
EZ
19/* Design overview:
20
21 For each watch request, we launch a separate worker thread. The
22 worker thread runs the watch_worker function, which issues an
23 asynchronous call to ReadDirectoryChangesW, and then waits for that
24 call to complete in SleepEx. Waiting in SleepEx puts the thread in
25 an alertable state, so it wakes up when either (a) the call to
26 ReadDirectoryChangesW completes, or (b) the main thread instructs
27 the worker thread to terminate by sending it an APC, see below.
28
29 When the ReadDirectoryChangesW call completes, its completion
30 routine watch_completion is automatically called. watch_completion
31 stashes the received file events in a buffer used to communicate
32 them to the main thread (using a critical section, so that several
33 threads could use the same buffer), posts a special message,
34 WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns.
35 That causes the SleepEx function call inside watch_worker to
36 return, and watch_worker then issues another call to
37 ReadDirectoryChangesW. (Except when it does not, see below.)
38
977c6479
EZ
39 In a GUI session, The WM_EMACS_FILENOTIFY message, posted to the
40 message queue gets dispatched to the main Emacs window procedure,
41 which queues it for processing by w32_read_socket. When
42 w32_read_socket sees this message, it accesses the buffer with file
43 notifications (using a critical section), extracts the information,
44 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
45 them into the input event queue to be processed by keyboard.c input
46 machinery (read_char via a call to kbd_buffer_get_event).
47
48 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
49 the main (a.k.a. "Lisp") thread instead, since there are no window
50 procedures in console programs. That message wakes up
51 MsgWaitForMultipleObjects inside sys_select, which then signals to
52 its caller that some keyboard input is available. This causes
53 w32_console_read_socket to be called, which accesses the buffer
54 with file notifications and stuffs them into the input event queue
55 for keyboard.c to process.
56
57 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
58 kbd_buffer_get_event, it is converted to a Lispy event that can be
59 bound to a command. The default binding is w32notify-handle-event,
60 defined on subr.el.
61
62 After w32_read_socket or w32_console_read_socket is done processing
63 the notifications, it resets a flag signaling to all watch worker
64 threads that the notifications buffer is available for more input.
c5c91b84
EZ
65
66 When the watch is removed by a call to w32notify-rm-watch, the main
67 thread requests that the worker thread terminates by queuing an APC
68 for the worker thread. The APC specifies the watch_end function to
69 be called. watch_end calls CancelIo on the outstanding
70 ReadDirectoryChangesW call and closes the handle on which the
71 watched directory was open. When watch_end returns, the
72 watch_completion function is called one last time with the
73 ERROR_OPERATION_ABORTED status, which causes it to clean up and set
74 a flag telling watch_worker to exit without issuing another
d6de1760
EZ
75 ReadDirectoryChangesW call. The main thread waits for some time
76 for the worker thread to exit, and if it doesn't, terminates it
77 forcibly. */
c5c91b84 78
6f011d81
EZ
79#include <stddef.h>
80#include <errno.h>
81
82/* must include CRT headers *before* config.h */
83#include <config.h>
84
85#include <windows.h>
86
87#include "lisp.h"
88#include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
eb3abb61 89#include "w32common.h" /* for OS version data */
6f011d81
EZ
90#include "w32.h" /* for w32_strerror */
91#include "coding.h"
92#include "keyboard.h"
93#include "frame.h" /* needed by termhooks.h */
94#include "termhooks.h" /* for FILE_NOTIFY_EVENT */
95
37a4dabe
EZ
96#define DIRWATCH_SIGNATURE 0x01233210
97
6f011d81
EZ
98struct notification {
99 BYTE *buf; /* buffer for ReadDirectoryChangesW */
100 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
101 BOOL subtree; /* whether to watch subdirectories */
102 DWORD filter; /* bit mask for events to watch */
103 char *watchee; /* the file we are interested in */
104 HANDLE dir; /* handle to the watched directory */
105 HANDLE thr; /* handle to the thread that watches */
0b86d359 106 volatile int terminate; /* if non-zero, request for the thread to terminate */
37a4dabe 107 unsigned signature;
6f011d81
EZ
108};
109
6f011d81 110/* Used for communicating notifications to the main thread. */
0b86d359 111volatile int notification_buffer_in_use;
6f011d81
EZ
112BYTE file_notifications[16384];
113DWORD notifications_size;
37a4dabe 114void *notifications_desc;
6f011d81
EZ
115
116static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
117static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
118static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
119
6f011d81
EZ
120/* Signal to the main thread that we have file notifications for it to
121 process. */
122static void
0b86d359
EZ
123send_notifications (BYTE *info, DWORD info_size, void *desc,
124 volatile int *terminate)
6f011d81
EZ
125{
126 int done = 0;
127 FRAME_PTR f = SELECTED_FRAME ();
128
6f011d81
EZ
129 /* A single buffer is used to communicate all notifications to the
130 main thread. Since both the main thread and several watcher
131 threads could be active at the same time, we use a critical area
132 and an "in-use" flag to synchronize them. A watcher thread can
133 only put its notifications in the buffer if it acquires the
134 critical area and finds the "in-use" flag reset. The main thread
135 resets the flag after it is done processing notifications.
136
137 FIXME: is there a better way of dealing with this? */
138 while (!done && !*terminate)
139 {
140 enter_crit ();
141 if (!notification_buffer_in_use)
142 {
143 if (info_size)
144 memcpy (file_notifications, info, info_size);
145 notifications_size = info_size;
37a4dabe 146 notifications_desc = desc;
977c6479
EZ
147 /* If PostMessage fails, the message queue is full. If that
148 happens, the last thing they will worry about is file
149 notifications. So we effectively discard the
150 notification in that case. */
151 if ((FRAME_TERMCAP_P (f)
152 /* We send the message to the main (a.k.a. "Lisp")
153 thread, where it will wake up MsgWaitForMultipleObjects
154 inside sys_select, causing it to report that there's
155 some keyboard input available. This will in turn cause
156 w32_console_read_socket to be called, which will pick
157 up the file notifications. */
158 && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0))
182b170f 159 || (FRAME_W32_P (f)
182b170f
EZ
160 && PostMessage (FRAME_W32_WINDOW (f),
161 WM_EMACS_FILENOTIFY, 0, 0)))
6f011d81
EZ
162 notification_buffer_in_use = 1;
163 done = 1;
6f011d81
EZ
164 }
165 leave_crit ();
166 if (!done)
167 Sleep (5);
168 }
169}
170
171/* An APC routine to cancel outstanding directory watch. Invoked by
172 the main thread via QueueUserAPC. This is needed because only the
173 thread that issued the ReadDirectoryChangesW call can call CancelIo
174 to cancel that. (CancelIoEx is only available since Vista, so we
175 cannot use it on XP.) */
176VOID CALLBACK
177watch_end (ULONG_PTR arg)
178{
179 HANDLE hdir = (HANDLE)arg;
180
181 if (hdir && hdir != INVALID_HANDLE_VALUE)
182 {
183 CancelIo (hdir);
184 CloseHandle (hdir);
185 }
186}
187
188/* A completion routine (a.k.a. APC function) for handling events read
189 by ReadDirectoryChangesW. Called by the OS when the thread which
190 issued the asynchronous ReadDirectoryChangesW call is in the
191 "alertable state", i.e. waiting inside SleepEx call. */
192VOID CALLBACK
193watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
194{
195 struct notification *dirwatch;
196
197 /* Who knows what happened? Perhaps the OVERLAPPED structure was
198 freed by someone already? In any case, we cannot do anything
199 with this request, so just punt and skip it. FIXME: should we
200 raise the 'terminate' flag in this case? */
201 if (!io_info)
202 return;
203
204 /* We have a pointer to our dirwatch structure conveniently stashed
205 away in the hEvent member of the OVERLAPPED struct. According to
206 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
207 of the OVERLAPPED structure is not used by the system, so you can
208 use it yourself." */
209 dirwatch = (struct notification *)io_info->hEvent;
210 if (status == ERROR_OPERATION_ABORTED)
211 {
212 /* We've been called because the main thread told us to issue
213 CancelIo on the directory we watch, and watch_end did so.
214 The directory handle is already closed. We should clean up
215 and exit, signalling to the thread worker routine not to
37a4dabe
EZ
216 issue another call to ReadDirectoryChangesW. Note that we
217 don't free the dirwatch object itself; this is done by the
218 main thread in remove_watch. */
6f011d81
EZ
219 xfree (dirwatch->buf);
220 dirwatch->buf = NULL;
221 xfree (dirwatch->io_info);
222 dirwatch->io_info = NULL;
223 xfree (dirwatch->watchee);
224 dirwatch->watchee = NULL;
7d605354 225 dirwatch->dir = NULL;
6f011d81
EZ
226 dirwatch->terminate = 1;
227 }
228 else
229 {
6f011d81 230 /* Tell the main thread we have notifications for it. */
37a4dabe 231 send_notifications (dirwatch->buf, bytes_ret, dirwatch,
6f011d81
EZ
232 &dirwatch->terminate);
233 }
234}
235
236/* Worker routine for the watch thread. */
237static DWORD WINAPI
238watch_worker (LPVOID arg)
239{
240 struct notification *dirwatch = (struct notification *)arg;
241
242 do {
243 BOOL status;
244 DWORD sleep_result;
245 DWORD bytes_ret = 0;
246
247 if (dirwatch->dir)
248 {
249 status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
250 dirwatch->subtree, dirwatch->filter,
251 &bytes_ret,
252 dirwatch->io_info, watch_completion);
253 if (!status)
254 {
c5c91b84 255 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
37a4dabe
EZ
256 /* We cannot remove the dirwatch object from watch_list,
257 because we are in a separate thread. So we free and
258 zero out all the pointers in the object, but do not
259 free the object itself. We also don't touch the
260 signature. This way, remove_watch can still identify
261 the object, remove it, and free its memory. */
6f011d81
EZ
262 xfree (dirwatch->buf);
263 dirwatch->buf = NULL;
264 xfree (dirwatch->io_info);
265 dirwatch->io_info = NULL;
266 CloseHandle (dirwatch->dir);
267 dirwatch->dir = NULL;
268 xfree (dirwatch->watchee);
269 dirwatch->watchee = NULL;
270 return 1;
271 }
272 }
273 /* Sleep indefinitely until awoken by the I/O completion, which
274 could be either a change notification or a cancellation of the
275 watch. */
276 sleep_result = SleepEx (INFINITE, TRUE);
6f011d81
EZ
277 } while (!dirwatch->terminate);
278
6f011d81
EZ
279 return 0;
280}
281
282/* Launch a thread to watch changes to FILE in a directory open on
283 handle HDIR. */
37a4dabe 284static struct notification *
6f011d81
EZ
285start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
286{
37a4dabe
EZ
287 struct notification *dirwatch = xzalloc (sizeof (struct notification));
288 HANDLE thr;
289
290 dirwatch->signature = DIRWATCH_SIGNATURE;
291 dirwatch->buf = xmalloc (16384);
292 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
6f011d81
EZ
293 /* Stash a pointer to dirwatch structure for use by the completion
294 routine. According to MSDN documentation of ReadDirectoryChangesW:
295 "The hEvent member of the OVERLAPPED structure is not used by the
296 system, so you can use it yourself." */
37a4dabe
EZ
297 dirwatch->io_info->hEvent = dirwatch;
298 dirwatch->subtree = subdirs;
299 dirwatch->filter = flags;
300 dirwatch->watchee = xstrdup (file);
301 dirwatch->terminate = 0;
302 dirwatch->dir = hdir;
6f011d81
EZ
303
304 /* See w32proc.c where it calls CreateThread for the story behind
305 the 2nd and 5th argument in the call to CreateThread. */
37a4dabe
EZ
306 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
307 0x00010000, NULL);
6f011d81 308
37a4dabe 309 if (!dirwatch->thr)
6f011d81 310 {
37a4dabe
EZ
311 xfree (dirwatch->buf);
312 xfree (dirwatch->io_info);
313 xfree (dirwatch->watchee);
314 xfree (dirwatch);
315 dirwatch = NULL;
6f011d81 316 }
37a4dabe 317 return dirwatch;
6f011d81
EZ
318}
319
320/* Called from the main thread to start watching FILE in PARENT_DIR,
321 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
37a4dabe
EZ
322 PARENT_DIR as well. Value is a pointer to 'struct notification'
323 used by the thread that watches the changes. */
324static struct notification *
6f011d81
EZ
325add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
326{
327 HANDLE hdir;
37a4dabe 328 struct notification *dirwatch = NULL;
6f011d81
EZ
329
330 if (!file || !*file)
331 return NULL;
332
333 hdir = CreateFile (parent_dir,
334 FILE_LIST_DIRECTORY,
335 /* FILE_SHARE_DELETE doesn't preclude other
336 processes from deleting files inside
337 parent_dir. */
338 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
339 NULL, OPEN_EXISTING,
340 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
341 NULL);
342 if (hdir == INVALID_HANDLE_VALUE)
343 return NULL;
344
37a4dabe
EZ
345 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
346 CloseHandle (hdir);
6f011d81 347
37a4dabe 348 return dirwatch;
6f011d81
EZ
349}
350
37a4dabe 351/* Stop watching a directory specified by a pointer to its dirwatch object. */
6f011d81 352static int
37a4dabe 353remove_watch (struct notification *dirwatch)
6f011d81 354{
37a4dabe 355 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
6f011d81
EZ
356 {
357 int i;
358 BOOL status;
359 DWORD exit_code, err;
360
361 /* Only the thread that issued the outstanding I/O call can call
362 CancelIo on it. (CancelIoEx is available only since Vista.)
363 So we need to queue an APC for the worker thread telling it
364 to terminate. */
37a4dabe 365 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
6f011d81
EZ
366 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
367 /* We also set the terminate flag, for when the thread is
368 waiting on the critical section that never gets acquired.
369 FIXME: is there a cleaner method? Using SleepEx there is a
370 no-no, as that will lead to recursive APC invocations and
371 stack overflow. */
37a4dabe 372 dirwatch->terminate = 1;
6f011d81
EZ
373 /* Wait for the thread to exit. FIXME: is there a better method
374 that is not overly complex? */
375 for (i = 0; i < 50; i++)
376 {
37a4dabe 377 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
6f011d81
EZ
378 && exit_code == STILL_ACTIVE))
379 break;
380 Sleep (10);
381 }
382 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
383 || exit_code == STILL_ACTIVE)
384 {
385 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
37a4dabe
EZ
386 {
387 TerminateThread (dirwatch->thr, 0);
388 if (dirwatch->dir)
389 CloseHandle (dirwatch->dir);
390 }
6f011d81
EZ
391 }
392
393 /* Clean up. */
37a4dabe 394 if (dirwatch->thr)
6f011d81 395 {
37a4dabe
EZ
396 CloseHandle (dirwatch->thr);
397 dirwatch->thr = NULL;
6f011d81 398 }
37a4dabe
EZ
399 xfree (dirwatch->buf);
400 xfree (dirwatch->io_info);
401 xfree (dirwatch->watchee);
402 xfree (dirwatch);
403
6f011d81
EZ
404 return 0;
405 }
406 else
407 {
37a4dabe 408 DebPrint (("Unknown dirwatch object!\n"));
6f011d81
EZ
409 return -1;
410 }
411}
412
413static DWORD
414filter_list_to_flags (Lisp_Object filter_list)
415{
416 DWORD flags = 0;
417
418 if (NILP (filter_list))
419 return flags;
420
421 if (!NILP (Fmember (Qfile_name, filter_list)))
422 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
423 if (!NILP (Fmember (Qdirectory_name, filter_list)))
424 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
425 if (!NILP (Fmember (Qattributes, filter_list)))
426 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
427 if (!NILP (Fmember (Qsize, filter_list)))
428 flags |= FILE_NOTIFY_CHANGE_SIZE;
429 if (!NILP (Fmember (Qlast_write_time, filter_list)))
430 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
431 if (!NILP (Fmember (Qlast_access_time, filter_list)))
432 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
433 if (!NILP (Fmember (Qcreation_time, filter_list)))
434 flags |= FILE_NOTIFY_CHANGE_CREATION;
435 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
436 flags |= FILE_NOTIFY_CHANGE_SECURITY;
437
438 return flags;
439}
440
441DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
442 Sw32notify_add_watch, 3, 3, 0,
443 doc: /* Add a watch for filesystem events pertaining to FILE.
444
445This arranges for filesystem events pertaining to FILE to be reported
446to Emacs. Use `w32notify-rm-watch' to cancel the watch.
447
448Value is a descriptor for the added watch, or nil if the file
449cannot be watched.
450
451FILTER is a list of conditions for reporting an event. It can include
452the following symbols:
453
454 'file-name' -- report file creation, deletion, or renaming
455 'directory-name' -- report directory creation, deletion, or renaming
456 'attributes' -- report changes in attributes
457 'size' -- report changes in file-size
458 'last-write-time' -- report changes in last-write time
459 'last-access-time' -- report changes in last-access time
460 'creation-time' -- report changes in creation time
461 'security-desc' -- report changes in security descriptor
462
463If FILE is a directory, and FILTER includes 'subtree', then all the
464subdirectories will also be watched and changes in them reported.
465
466When any event happens that satisfies the conditions specified by
467FILTER, Emacs will call the CALLBACK function passing it a single
468argument EVENT, which is of the form
469
470 (DESCRIPTOR ACTION FILE)
471
472DESCRIPTOR is the same object as the one returned by this function.
473ACTION is the description of the event. It could be any one of the
474following:
475
476 'added' -- FILE was added
477 'removed' -- FILE was deleted
478 'modified' -- FILE's contents or its attributes were modified
479 'renamed-from' -- a file was renamed whose old name was FILE
480 'renamed-to' -- a file was renamed and its new name is FILE
481
482FILE is the name of the file whose event is being reported. */)
483 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
484{
485 Lisp_Object encoded_file, watch_object, watch_descriptor;
486 char parent_dir[MAX_PATH], *basename;
487 size_t fn_len;
6f011d81
EZ
488 DWORD flags;
489 BOOL subdirs = FALSE;
37a4dabe 490 struct notification *dirwatch = NULL;
6f011d81
EZ
491 Lisp_Object lisp_errstr;
492 char *errstr;
493
494 CHECK_LIST (filter);
495
496 /* The underlying features are available only since XP. */
497 if (os_subtype == OS_9X
498 || (w32_major_version == 5 && w32_major_version < 1))
499 {
500 errno = ENOSYS;
501 report_file_error ("Watching filesystem events is not supported",
502 Qnil);
503 }
504
37a4dabe 505 /* We need a full absolute file name of FILE, and we need to remove
6f011d81
EZ
506 any trailing slashes from it, so that GetFullPathName below gets
507 the basename part correctly. */
508 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
509 encoded_file = ENCODE_FILE (file);
510
511 fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
512 &basename);
513 if (!fn_len)
514 {
515 errstr = w32_strerror (0);
516 errno = EINVAL;
517 if (!NILP (Vlocale_coding_system))
518 lisp_errstr
519 = code_convert_string_norecord (build_unibyte_string (errstr),
520 Vlocale_coding_system, 0);
521 else
522 lisp_errstr = build_string (errstr);
523 report_file_error ("GetFullPathName failed",
524 Fcons (lisp_errstr, Fcons (file, Qnil)));
525 }
526 /* We need the parent directory without the slash that follows it.
527 If BASENAME is NULL, the argument was the root directory on its
528 drive. */
529 if (basename)
530 basename[-1] = '\0';
531 else
532 subdirs = TRUE;
533
534 if (!NILP (Fmember (Qsubtree, filter)))
535 subdirs = TRUE;
536
537 flags = filter_list_to_flags (filter);
538
37a4dabe
EZ
539 dirwatch = add_watch (parent_dir, basename, subdirs, flags);
540 if (!dirwatch)
6f011d81
EZ
541 {
542 DWORD err = GetLastError ();
543
544 errno = EINVAL;
545 if (err)
546 {
547 errstr = w32_strerror (err);
548 if (!NILP (Vlocale_coding_system))
549 lisp_errstr
550 = code_convert_string_norecord (build_unibyte_string (errstr),
551 Vlocale_coding_system, 0);
552 else
553 lisp_errstr = build_string (errstr);
554 report_file_error ("Cannot watch file",
555 Fcons (lisp_errstr, Fcons (file, Qnil)));
556 }
557 else
558 report_file_error ("Cannot watch file", Fcons (file, Qnil));
559 }
560 /* Store watch object in watch list. */
0b86d359 561 watch_descriptor = XIL ((EMACS_INT)dirwatch);
6f011d81
EZ
562 watch_object = Fcons (watch_descriptor, callback);
563 watch_list = Fcons (watch_object, watch_list);
564
565 return watch_descriptor;
566}
567
568DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
569 Sw32notify_rm_watch, 1, 1, 0,
570 doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
571
572WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
573 (Lisp_Object watch_descriptor)
574{
575 Lisp_Object watch_object;
0b86d359
EZ
576 struct notification *dirwatch;
577 int status = -1;
6f011d81 578
37a4dabe
EZ
579 /* Remove the watch object from watch list. Do this before freeing
580 the object, do that even if we fail to free it, watch_list is
581 kept free of junk. */
6f011d81
EZ
582 watch_object = Fassoc (watch_descriptor, watch_list);
583 if (!NILP (watch_object))
0b86d359
EZ
584 {
585 watch_list = Fdelete (watch_object, watch_list);
586 dirwatch = (struct notification *)XLI (watch_descriptor);
587 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
588 status = remove_watch (dirwatch);
589 }
6f011d81 590
0b86d359 591 if (status == -1)
37a4dabe
EZ
592 report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
593 Qnil));
594
6f011d81
EZ
595 return Qnil;
596}
597
598Lisp_Object
0b86d359 599w32_get_watch_object (void *desc)
37a4dabe 600{
0b86d359
EZ
601 Lisp_Object descriptor = XIL ((EMACS_INT)desc);
602
d884121b
EZ
603 /* This is called from the input queue handling code, inside a
604 critical section, so we cannot possibly QUIT if watch_list is not
605 in the right condition. */
0b86d359 606 return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
37a4dabe
EZ
607}
608
609void
610globals_of_w32notify (void)
6f011d81 611{
37a4dabe 612 watch_list = Qnil;
6f011d81
EZ
613}
614
615void
616syms_of_w32notify (void)
617{
618 DEFSYM (Qfile_name, "file-name");
619 DEFSYM (Qdirectory_name, "directory-name");
620 DEFSYM (Qattributes, "attributes");
621 DEFSYM (Qsize, "size");
622 DEFSYM (Qlast_write_time, "last-write-time");
623 DEFSYM (Qlast_access_time, "last-access-time");
624 DEFSYM (Qcreation_time, "creation-time");
625 DEFSYM (Qsecurity_desc, "security-desc");
626 DEFSYM (Qsubtree, "subtree");
627
628 defsubr (&Sw32notify_add_watch);
629 defsubr (&Sw32notify_rm_watch);
630
631 staticpro (&watch_list);
632
633 Fprovide (intern_c_string ("w32notify"), Qnil);
634}