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