1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012-2017 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
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 (at
9 your option) any later version.
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.
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/>. */
19 /* Written by Eli Zaretskii <eliz@gnu.org>.
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 calls
26 WaitForSingleObjectEx to wait that an event be signaled
27 to terminate the thread.
28 Waiting with WaitForSingleObjectEx puts the thread in an
29 "alertable" state, so it wakes up when either (a) the call to
30 ReadDirectoryChangesW completes, or (b) the main thread instructs
31 the worker thread to terminate by signaling an event, see below.
33 When the ReadDirectoryChangesW call completes, its completion
34 routine watch_completion is automatically called. watch_completion
35 stashes the received file events in a linked list used to
36 communicate them to the main thread (using a critical section, so
37 that several threads could alter the same linked list), posts a
38 special message, WM_EMACS_FILENOTIFY, to the Emacs's message queue,
39 and returns. That causes the WaitForSingleObjectEx function call
40 inside watch_worker to return, but the thread won't terminate until
41 the event telling to do so will be signaled. The completion
42 routine issued another call to ReadDirectoryChangesW as quickly as
43 possible. (Except when it does not, see below.)
45 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
46 message queue gets dispatched to the main Emacs window procedure,
47 which queues it for processing by w32_read_socket. When
48 w32_read_socket sees this message, it accesses the linked list with file
49 notifications (using a critical section), extracts the information,
50 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
51 them into the input event queue to be processed by keyboard.c input
52 machinery (read_char via a call to kbd_buffer_get_event).
54 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
55 the main (a.k.a. "Lisp") thread instead, since there are no window
56 procedures in console programs. That message wakes up
57 MsgWaitForMultipleObjects inside sys_select, which then signals to
58 its caller that some keyboard input is available. This causes
59 w32_console_read_socket to be called, which accesses the linked list
60 with file notifications and stuffs them into the input event queue
61 for keyboard.c to process.
63 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
64 kbd_buffer_get_event, it is converted to a Lispy event that can be
65 bound to a command. The default binding is file-notify-handle-event,
68 Routines w32_read_socket or w32_console_read_socket process notifications
69 sets as long as some are available.
71 When the watch is removed by a call to w32notify-rm-watch, the main
72 thread requests that the worker thread terminates by signaling the
73 appropriate event and queuing an APC for the worker thread. The
74 APC specifies the watch_end function to be called. watch_end calls
75 CancelIo on the outstanding ReadDirectoryChangesW call. When
76 watch_end returns, the watch_completion function is called one last
77 time with the ERROR_OPERATION_ABORTED status, which causes it to
78 clean up and set a flag telling watch_worker to exit without
79 issuing another ReadDirectoryChangesW call. Since watch_worker is
80 the thread procedure of the worker thread, exiting it causes the
81 thread to exit. The main thread waits for some time for the worker
82 thread to exit, and if it doesn't, terminates it forcibly. */
84 #define DEFER_MS_W32_H
90 /* Include CRT headers *before* ms-w32.h. */
96 #include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
97 #include "w32common.h" /* for OS version data */
98 #include "w32.h" /* for w32_strerror */
100 #include "keyboard.h"
101 #include "frame.h" /* needed by termhooks.h */
102 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
104 #define DIRWATCH_BUFFER_SIZE 16384
105 #define DIRWATCH_SIGNATURE 0x01233210
107 struct notification
{
108 BYTE
*buf
; /* buffer for ReadDirectoryChangesW */
109 OVERLAPPED
*io_info
; /* the OVERLAPPED structure for async I/O */
110 BOOL subtree
; /* whether to watch subdirectories */
111 DWORD filter
; /* bit mask for events to watch */
112 char *watchee
; /* the file we are interested in, UTF-8 encoded */
113 HANDLE dir
; /* handle to the watched directory */
114 HANDLE thr
; /* handle to the thread that watches */
115 HANDLE terminate
; /* event signaling the thread to terminate */
119 /* Used for communicating notifications to the main thread. */
120 struct notifications_set
*notifications_set_head
;
122 static Lisp_Object watch_list
;
124 /* Signal to the main thread that we have file notifications for it to
127 send_notifications (struct notifications_set
*ns
)
129 struct frame
*f
= SELECTED_FRAME ();
131 /* We add the current notification set to the linked list. Use the
132 critical section to make sure only one thread will access the
135 ns
->next
= notifications_set_head
;
136 ns
->prev
= notifications_set_head
->prev
;
138 notifications_set_head
->prev
= ns
;
141 /* If PostMessage fails, the message queue is full. If that
142 happens, the last thing they will worry about is file
143 notifications. So we effectively discard the notification in
145 if (FRAME_TERMCAP_P (f
))
146 /* We send the message to the main (a.k.a. "Lisp") thread, where
147 it will wake up MsgWaitForMultipleObjects inside sys_select,
148 causing it to report that there's some keyboard input
149 available. This will in turn cause w32_console_read_socket to
150 be called, which will pick up the file notifications. */
151 PostThreadMessage (dwMainThreadId
, WM_EMACS_FILENOTIFY
, 0, 0);
152 else if (FRAME_W32_P (f
))
153 PostMessage (FRAME_W32_WINDOW (f
),
154 WM_EMACS_FILENOTIFY
, 0, 0);
155 /* When we are running in batch mode, there's no one to send a
156 message, so we just signal the data is available and hope
157 sys_select will be called soon and will read the data. */
159 else if (FRAME_INITIAL_P (f
) && noninteractive
)
164 /* An APC routine to cancel outstanding directory watch. Invoked by
165 the main thread via QueueUserAPC. This is needed because only the
166 thread that issued the ReadDirectoryChangesW call can call CancelIo
167 to cancel that. (CancelIoEx is only available since Vista, so we
168 cannot use it on XP.) */
169 VOID CALLBACK
watch_end (ULONG_PTR
);
172 watch_end (ULONG_PTR arg
)
174 HANDLE hdir
= (HANDLE
)arg
;
176 if (hdir
&& hdir
!= INVALID_HANDLE_VALUE
)
180 /* A completion routine (a.k.a. "APC function") for handling events
181 read by ReadDirectoryChangesW. Called by the OS when the thread
182 which issued the asynchronous ReadDirectoryChangesW call is in the
183 "alertable state", i.e. waiting inside SleepEx call. */
184 VOID CALLBACK
watch_completion (DWORD
, DWORD
, OVERLAPPED
*);
187 watch_completion (DWORD status
, DWORD bytes_ret
, OVERLAPPED
*io_info
)
189 struct notification
*dirwatch
;
191 struct notifications_set
*ns
= NULL
;
192 BOOL terminate
= FALSE
;
194 /* Who knows what happened? Perhaps the OVERLAPPED structure was
195 freed by someone already? In any case, we cannot do anything
196 with this request, so just punt and skip it. FIXME: should we
197 raise the 'terminate' flag in this case? */
200 DebPrint(("watch_completion: io_info is null.\n"));
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
209 dirwatch
= (struct notification
*)io_info
->hEvent
;
211 if (status
== ERROR_OPERATION_ABORTED
)
213 /* We've been called because the main thread told us to issue
214 CancelIo on the directory we watch, and watch_end did so.
215 We must exit, without issuing another call to
216 ReadDirectoryChangesW. */
220 /* We allocate a new set of notifications to be linked to the linked
221 list of notifications set. This will be processed by Emacs event
222 loop in the main thread. We need to duplicate the notifications
223 buffer, but not the dirwatch structure. */
225 /* Implementation note: In general, allocating memory in non-main
226 threads is a no-no in Emacs. We certainly cannot call xmalloc
227 and friends, because it can longjmp when allocation fails, which
228 will crash Emacs because the jmp_buf is set up to a location on
229 the main thread's stack. However, we can call 'malloc' directly,
230 since that is redirected to HeapAlloc that uses our private heap,
231 see w32heap.c, and that is thread-safe. */
232 ns
= malloc (sizeof(struct notifications_set
));
235 memset (ns
, 0, sizeof(struct notifications_set
));
236 ns
->notifications
= malloc (bytes_ret
);
237 if (ns
->notifications
)
239 memcpy (ns
->notifications
, dirwatch
->buf
, bytes_ret
);
240 ns
->size
= bytes_ret
;
250 DebPrint(("Out of memory. Notifications lost."));
252 /* Calling ReadDirectoryChangesW quickly to watch again for new
254 if (!ReadDirectoryChangesW (dirwatch
->dir
, dirwatch
->buf
,
255 DIRWATCH_BUFFER_SIZE
, dirwatch
->subtree
,
256 dirwatch
->filter
, &_bytes
, dirwatch
->io_info
,
259 DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
260 /* If this call fails, it means that the directory is not
261 watchable any more. We need to terminate the worker thread.
262 Still, we will wait until the current notifications have been
263 sent to the main thread. */
268 send_notifications(ns
);
270 /* If we were asked to terminate the thread, then fire the event. */
272 SetEvent(dirwatch
->terminate
);
275 /* Worker routine for the watch thread. */
277 watch_worker (LPVOID arg
)
279 struct notification
*dirwatch
= (struct notification
*)arg
;
286 bErr
= ReadDirectoryChangesW (dirwatch
->dir
, dirwatch
->buf
,
287 DIRWATCH_BUFFER_SIZE
, dirwatch
->subtree
,
288 dirwatch
->filter
, &_bytes
,
289 dirwatch
->io_info
, watch_completion
);
292 DebPrint (("ReadDirectoryChangesW: %lu\n", GetLastError ()));
293 /* We cannot remove the dirwatch object from watch_list,
294 because we are in a separate thread. For the same
295 reason, we also cannot free memory consumed by the
296 buffers allocated for the dirwatch object. So we close
297 the directory handle, but do not free the object itself
298 or its buffers. We also don't touch the signature. This
299 way, remove_watch can still identify the object, remove
300 it, and free its memory. */
301 CloseHandle (dirwatch
->dir
);
302 dirwatch
->dir
= NULL
;
308 status
= WaitForSingleObjectEx(dirwatch
->terminate
, INFINITE
, TRUE
);
309 } while (status
== WAIT_IO_COMPLETION
);
311 /* The thread is about to terminate, so we clean up the dir handle. */
312 CloseHandle (dirwatch
->dir
);
313 dirwatch
->dir
= NULL
;
317 /* Launch a thread to watch changes to FILE in a directory open on
319 static struct notification
*
320 start_watching (const char *file
, HANDLE hdir
, BOOL subdirs
, DWORD flags
)
322 struct notification
*dirwatch
= xzalloc (sizeof (struct notification
));
324 dirwatch
->signature
= DIRWATCH_SIGNATURE
;
325 dirwatch
->buf
= xmalloc (DIRWATCH_BUFFER_SIZE
);
326 dirwatch
->io_info
= xzalloc (sizeof(OVERLAPPED
));
327 /* Stash a pointer to dirwatch structure for use by the completion
328 routine. According to MSDN documentation of ReadDirectoryChangesW:
329 "The hEvent member of the OVERLAPPED structure is not used by the
330 system, so you can use it yourself." */
331 dirwatch
->io_info
->hEvent
= dirwatch
;
332 dirwatch
->subtree
= subdirs
;
333 dirwatch
->filter
= flags
;
334 dirwatch
->watchee
= xstrdup (file
);
336 dirwatch
->terminate
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
338 dirwatch
->dir
= hdir
;
340 /* See w32proc.c where it calls CreateThread for the story behind
341 the 2nd and 5th argument in the call to CreateThread. */
342 dirwatch
->thr
= CreateThread (NULL
, 64 * 1024, watch_worker
, (void *)dirwatch
,
347 CloseHandle(dirwatch
->terminate
);
348 xfree (dirwatch
->buf
);
349 xfree (dirwatch
->io_info
);
350 xfree (dirwatch
->watchee
);
356 /* Called from the main thread to start watching FILE in PARENT_DIR,
357 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
358 PARENT_DIR as well. Value is a pointer to 'struct notification'
359 used by the thread that watches the changes. */
360 static struct notification
*
361 add_watch (const char *parent_dir
, const char *file
, BOOL subdirs
, DWORD flags
)
364 struct notification
*dirwatch
= NULL
;
369 if (w32_unicode_filenames
)
371 wchar_t dir_w
[MAX_PATH
], file_w
[MAX_PATH
];
373 filename_to_utf16 (parent_dir
, dir_w
);
375 filename_to_utf16 (file
, file_w
);
379 hdir
= CreateFileW (dir_w
,
381 /* FILE_SHARE_DELETE doesn't preclude other
382 processes from deleting files inside
384 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
386 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
,
391 char dir_a
[MAX_PATH
], file_a
[MAX_PATH
];
393 filename_to_ansi (parent_dir
, dir_a
);
395 filename_to_ansi (file
, file_a
);
399 hdir
= CreateFileA (dir_a
,
401 FILE_SHARE_READ
|FILE_SHARE_WRITE
|FILE_SHARE_DELETE
,
403 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
,
406 if (hdir
== INVALID_HANDLE_VALUE
)
409 if ((dirwatch
= start_watching (file
, hdir
, subdirs
, flags
)) == NULL
)
412 dirwatch
->dir
= NULL
;
418 /* Stop watching a directory specified by a pointer to its dirwatch object. */
420 remove_watch (struct notification
*dirwatch
)
422 if (dirwatch
&& dirwatch
->signature
== DIRWATCH_SIGNATURE
)
426 DWORD exit_code
= 0, err
;
428 /* Only the thread that issued the outstanding I/O call can call
429 CancelIo on it. (CancelIoEx is available only since Vista.)
430 So we need to queue an APC for the worker thread telling it
432 if (!QueueUserAPC (watch_end
, dirwatch
->thr
, (ULONG_PTR
)dirwatch
->dir
))
433 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
435 /* We also signal the thread that it can terminate. */
436 SetEvent(dirwatch
->terminate
);
438 /* Wait for the thread to exit. FIXME: is there a better method
439 that is not overly complex? */
440 for (i
= 0; i
< 50; i
++)
442 if (!((status
= GetExitCodeThread (dirwatch
->thr
, &exit_code
))
443 && exit_code
== STILL_ACTIVE
))
448 if ((status
== FALSE
&& (err
= GetLastError ()) == ERROR_INVALID_HANDLE
)
449 || exit_code
== STILL_ACTIVE
)
451 if (!(status
== FALSE
&& err
== ERROR_INVALID_HANDLE
))
453 DebPrint(("Forcing thread termination.\n"));
454 TerminateThread (dirwatch
->thr
, 0);
456 CloseHandle (dirwatch
->dir
);
463 CloseHandle (dirwatch
->thr
);
464 dirwatch
->thr
= NULL
;
466 CloseHandle(dirwatch
->terminate
);
467 xfree (dirwatch
->buf
);
468 xfree (dirwatch
->io_info
);
469 xfree (dirwatch
->watchee
);
475 DebPrint (("Unknown dirwatch object!\n"));
481 filter_list_to_flags (Lisp_Object filter_list
)
485 if (NILP (filter_list
))
488 if (!NILP (Fmember (Qfile_name
, filter_list
)))
489 flags
|= FILE_NOTIFY_CHANGE_FILE_NAME
;
490 if (!NILP (Fmember (Qdirectory_name
, filter_list
)))
491 flags
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
492 if (!NILP (Fmember (Qattributes
, filter_list
)))
493 flags
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
494 if (!NILP (Fmember (Qsize
, filter_list
)))
495 flags
|= FILE_NOTIFY_CHANGE_SIZE
;
496 if (!NILP (Fmember (Qlast_write_time
, filter_list
)))
497 flags
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
498 if (!NILP (Fmember (Qlast_access_time
, filter_list
)))
499 flags
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
500 if (!NILP (Fmember (Qcreation_time
, filter_list
)))
501 flags
|= FILE_NOTIFY_CHANGE_CREATION
;
502 if (!NILP (Fmember (Qsecurity_desc
, filter_list
)))
503 flags
|= FILE_NOTIFY_CHANGE_SECURITY
;
508 DEFUN ("w32notify-add-watch", Fw32notify_add_watch
,
509 Sw32notify_add_watch
, 3, 3, 0,
510 doc
: /* Add a watch for filesystem events pertaining to FILE.
512 This arranges for filesystem events pertaining to FILE to be reported
513 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
515 Value is a descriptor for the added watch. If the file cannot be
516 watched for some reason, this function signals a `file-error' error.
518 FILTER is a list of conditions for reporting an event. It can include
519 the following symbols:
521 'file-name' -- report file creation, deletion, or renaming
522 'directory-name' -- report directory creation, deletion, or renaming
523 'attributes' -- report changes in attributes
524 'size' -- report changes in file-size
525 'last-write-time' -- report changes in last-write time
526 'last-access-time' -- report changes in last-access time
527 'creation-time' -- report changes in creation time
528 'security-desc' -- report changes in security descriptor
530 If FILE is a directory, and FILTER includes 'subtree', then all the
531 subdirectories will also be watched and changes in them reported.
533 When any event happens that satisfies the conditions specified by
534 FILTER, Emacs will call the CALLBACK function passing it a single
535 argument EVENT, which is of the form
537 (DESCRIPTOR ACTION FILE)
539 DESCRIPTOR is the same object as the one returned by this function.
540 ACTION is the description of the event. It could be any one of the
543 'added' -- FILE was added
544 'removed' -- FILE was deleted
545 'modified' -- FILE's contents or its attributes were modified
546 'renamed-from' -- a file was renamed whose old name was FILE
547 'renamed-to' -- a file was renamed and its new name is FILE
549 FILE is the name of the file whose event is being reported.
551 Note that some networked filesystems, such as Samba-mounted Unix
552 volumes, might not send notifications about file changes. In these
553 cases, this function will return a valid descriptor, but notifications
554 will never come in. Volumes shared from remote Windows machines do
555 generate notifications correctly, though. */)
556 (Lisp_Object file
, Lisp_Object filter
, Lisp_Object callback
)
558 Lisp_Object dirfn
, basefn
, watch_object
, watch_descriptor
;
560 BOOL subdirs
= FALSE
;
561 struct notification
*dirwatch
= NULL
;
562 Lisp_Object lisp_errstr
;
567 /* The underlying features are available only since XP. */
568 if (os_subtype
== OS_9X
569 || (w32_major_version
== 5 && w32_minor_version
< 1))
572 report_file_notify_error ("Watching filesystem events is not supported",
576 /* filenotify.el always passes us a directory, either the parent
577 directory of a file to be watched, or the directory to be
579 file
= Fdirectory_file_name (Fexpand_file_name (file
, Qnil
));
580 if (NILP (Ffile_directory_p (file
)))
582 /* This should only happen if we are called directly, not via
583 filenotify.el. If BASEFN is empty, the argument was the root
584 directory on its drive. */
585 dirfn
= ENCODE_FILE (Ffile_name_directory (file
));
586 basefn
= ENCODE_FILE (Ffile_name_nondirectory (file
));
587 if (*SDATA (basefn
) == '\0')
592 dirfn
= ENCODE_FILE (file
);
596 if (!NILP (Fmember (Qsubtree
, filter
)))
599 flags
= filter_list_to_flags (filter
);
601 dirwatch
= add_watch (SSDATA (dirfn
), NILP (basefn
) ? "" : SSDATA (basefn
),
605 DWORD err
= GetLastError ();
610 errstr
= w32_strerror (err
);
611 if (!NILP (Vlocale_coding_system
))
613 = code_convert_string_norecord (build_unibyte_string (errstr
),
614 Vlocale_coding_system
, 0);
616 lisp_errstr
= build_string (errstr
);
617 report_file_notify_error ("Cannot watch file",
618 Fcons (lisp_errstr
, Fcons (file
, Qnil
)));
621 report_file_notify_error ("Cannot watch file", Fcons (file
, Qnil
));
623 /* Store watch object in watch list. */
624 watch_descriptor
= make_pointer_integer (dirwatch
);
625 watch_object
= Fcons (watch_descriptor
, callback
);
626 watch_list
= Fcons (watch_object
, watch_list
);
628 return watch_descriptor
;
631 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch
,
632 Sw32notify_rm_watch
, 1, 1, 0,
633 doc
: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
635 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
636 (Lisp_Object watch_descriptor
)
638 Lisp_Object watch_object
;
639 struct notification
*dirwatch
;
642 /* Remove the watch object from watch list. Do this before freeing
643 the object, do that even if we fail to free it, watch_list is
644 kept free of junk. */
645 watch_object
= Fassoc (watch_descriptor
, watch_list
);
646 if (!NILP (watch_object
))
648 watch_list
= Fdelete (watch_object
, watch_list
);
649 dirwatch
= (struct notification
*)XINTPTR (watch_descriptor
);
650 if (w32_valid_pointer_p (dirwatch
, sizeof(struct notification
)))
651 status
= remove_watch (dirwatch
);
655 report_file_notify_error ("Invalid watch descriptor",
656 Fcons (watch_descriptor
, Qnil
));
662 w32_get_watch_object (void *desc
)
664 Lisp_Object descriptor
= make_pointer_integer (desc
);
666 /* This is called from the input queue handling code, inside a
667 critical section, so we cannot possibly quit if watch_list is not
668 in the right condition. */
669 return NILP (watch_list
) ? Qnil
: assoc_no_quit (descriptor
, watch_list
);
672 DEFUN ("w32notify-valid-p", Fw32notify_valid_p
, Sw32notify_valid_p
, 1, 1, 0,
673 doc
: /* Check a watch specified by its WATCH-DESCRIPTOR for validity.
675 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.
677 A watch can become invalid if the directory it watches is deleted, or if
678 the watcher thread exits abnormally for any other reason. Removing the
679 watch by calling `w32notify-rm-watch' also makes it invalid. */)
680 (Lisp_Object watch_descriptor
)
682 Lisp_Object watch_object
= Fassoc (watch_descriptor
, watch_list
);
684 if (!NILP (watch_object
))
686 struct notification
*dirwatch
=
687 (struct notification
*)XINTPTR (watch_descriptor
);
688 if (w32_valid_pointer_p (dirwatch
, sizeof(struct notification
))
689 && dirwatch
->dir
!= NULL
)
697 globals_of_w32notify (void)
703 syms_of_w32notify (void)
705 DEFSYM (Qfile_name
, "file-name");
706 DEFSYM (Qdirectory_name
, "directory-name");
707 DEFSYM (Qattributes
, "attributes");
708 DEFSYM (Qlast_write_time
, "last-write-time");
709 DEFSYM (Qlast_access_time
, "last-access-time");
710 DEFSYM (Qcreation_time
, "creation-time");
711 DEFSYM (Qsecurity_desc
, "security-desc");
712 DEFSYM (Qsubtree
, "subtree");
714 defsubr (&Sw32notify_add_watch
);
715 defsubr (&Sw32notify_rm_watch
);
716 defsubr (&Sw32notify_valid_p
);
718 staticpro (&watch_list
);
720 Fprovide (intern_c_string ("w32notify"), Qnil
);