Commit | Line | Data |
---|---|---|
6f011d81 | 1 | /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API. |
ba318903 | 2 | Copyright (C) 2012-2014 Free Software Foundation, Inc. |
6f011d81 EZ |
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 | ||
f282b9f7 EZ |
19 | /* Written by Eli Zaretskii <eliz@gnu.org>. |
20 | ||
21 | Design overview: | |
c5c91b84 EZ |
22 | |
23 | For each watch request, we launch a separate worker thread. The | |
24 | worker thread runs the watch_worker function, which issues an | |
f282b9f7 EZ |
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. | |
c5c91b84 EZ |
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 | ||
a0eb10b3 | 42 | In a GUI session, the WM_EMACS_FILENOTIFY message posted to the |
977c6479 EZ |
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 | |
102ae68d | 62 | bound to a command. The default binding is file-notify-handle-event, |
977c6479 EZ |
63 | defined on subr.el. |
64 | ||
f282b9f7 EZ |
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. | |
c5c91b84 EZ |
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 | |
f282b9f7 EZ |
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. */ | |
c5c91b84 | 83 | |
6f011d81 EZ |
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 */ | |
eb3abb61 | 94 | #include "w32common.h" /* for OS version data */ |
6f011d81 EZ |
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 | ||
37a4dabe EZ |
101 | #define DIRWATCH_SIGNATURE 0x01233210 |
102 | ||
6f011d81 EZ |
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 */ | |
439b1ae8 | 108 | char *watchee; /* the file we are interested in, UTF-8 encoded */ |
6f011d81 EZ |
109 | HANDLE dir; /* handle to the watched directory */ |
110 | HANDLE thr; /* handle to the thread that watches */ | |
0b86d359 | 111 | volatile int terminate; /* if non-zero, request for the thread to terminate */ |
37a4dabe | 112 | unsigned signature; |
6f011d81 EZ |
113 | }; |
114 | ||
6f011d81 | 115 | /* Used for communicating notifications to the main thread. */ |
0b86d359 | 116 | volatile int notification_buffer_in_use; |
6f011d81 EZ |
117 | BYTE file_notifications[16384]; |
118 | DWORD notifications_size; | |
37a4dabe | 119 | void *notifications_desc; |
6f011d81 EZ |
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 | ||
6f011d81 EZ |
125 | /* Signal to the main thread that we have file notifications for it to |
126 | process. */ | |
127 | static void | |
0b86d359 EZ |
128 | send_notifications (BYTE *info, DWORD info_size, void *desc, |
129 | volatile int *terminate) | |
6f011d81 EZ |
130 | { |
131 | int done = 0; | |
a10c8269 | 132 | struct frame *f = SELECTED_FRAME (); |
6f011d81 | 133 | |
6f011d81 EZ |
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; | |
37a4dabe | 151 | notifications_desc = desc; |
977c6479 EZ |
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)) | |
182b170f | 164 | || (FRAME_W32_P (f) |
182b170f | 165 | && PostMessage (FRAME_W32_WINDOW (f), |
9c099ca7 EZ |
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)) | |
6f011d81 EZ |
172 | notification_buffer_in_use = 1; |
173 | done = 1; | |
6f011d81 EZ |
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 | ||
f282b9f7 EZ |
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 | |
6f011d81 EZ |
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 | |
4a0e1924 | 225 | and exit, signaling to the thread worker routine not to |
37a4dabe | 226 | issue another call to ReadDirectoryChangesW. Note that we |
9e1821d3 EZ |
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. */ | |
7d605354 | 231 | dirwatch->dir = NULL; |
6f011d81 EZ |
232 | dirwatch->terminate = 1; |
233 | } | |
234 | else | |
235 | { | |
6f011d81 | 236 | /* Tell the main thread we have notifications for it. */ |
37a4dabe | 237 | send_notifications (dirwatch->buf, bytes_ret, dirwatch, |
6f011d81 EZ |
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; | |
6f011d81 EZ |
250 | DWORD bytes_ret = 0; |
251 | ||
252 | if (dirwatch->dir) | |
253 | { | |
254 | status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384, | |
255 | dirwatch->subtree, dirwatch->filter, | |
256 | &bytes_ret, | |
257 | dirwatch->io_info, watch_completion); | |
258 | if (!status) | |
259 | { | |
c5c91b84 | 260 | DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ())); |
37a4dabe | 261 | /* We cannot remove the dirwatch object from watch_list, |
9e1821d3 EZ |
262 | because we are in a separate thread. For the same |
263 | reason, we also cannot free memory consumed by the | |
264 | buffers allocated for the dirwatch object. So we close | |
265 | the directory handle, but do not free the object itself | |
266 | or its buffers. We also don't touch the signature. | |
267 | This way, remove_watch can still identify the object, | |
268 | remove it, and free its memory. */ | |
6f011d81 EZ |
269 | CloseHandle (dirwatch->dir); |
270 | dirwatch->dir = NULL; | |
6f011d81 EZ |
271 | return 1; |
272 | } | |
273 | } | |
274 | /* Sleep indefinitely until awoken by the I/O completion, which | |
275 | could be either a change notification or a cancellation of the | |
276 | watch. */ | |
5af73b0f | 277 | SleepEx (INFINITE, TRUE); |
6f011d81 EZ |
278 | } while (!dirwatch->terminate); |
279 | ||
6f011d81 EZ |
280 | return 0; |
281 | } | |
282 | ||
283 | /* Launch a thread to watch changes to FILE in a directory open on | |
284 | handle HDIR. */ | |
37a4dabe | 285 | static struct notification * |
6f011d81 EZ |
286 | start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags) |
287 | { | |
37a4dabe | 288 | struct notification *dirwatch = xzalloc (sizeof (struct notification)); |
37a4dabe EZ |
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. */ | |
324 | static struct notification * | |
6f011d81 EZ |
325 | add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags) |
326 | { | |
327 | HANDLE hdir; | |
37a4dabe | 328 | struct notification *dirwatch = NULL; |
6f011d81 | 329 | |
fb6a5d68 | 330 | if (!file) |
6f011d81 EZ |
331 | return NULL; |
332 | ||
439b1ae8 EZ |
333 | if (w32_unicode_filenames) |
334 | { | |
335 | wchar_t dir_w[MAX_PATH], file_w[MAX_PATH]; | |
336 | ||
337 | filename_to_utf16 (parent_dir, dir_w); | |
338 | if (*file) | |
339 | filename_to_utf16 (file, file_w); | |
340 | else | |
341 | file_w[0] = 0; | |
342 | ||
343 | hdir = CreateFileW (dir_w, | |
344 | FILE_LIST_DIRECTORY, | |
345 | /* FILE_SHARE_DELETE doesn't preclude other | |
346 | processes from deleting files inside | |
347 | parent_dir. */ | |
348 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, | |
349 | NULL, OPEN_EXISTING, | |
350 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, | |
351 | NULL); | |
352 | } | |
353 | else | |
354 | { | |
355 | char dir_a[MAX_PATH], file_a[MAX_PATH]; | |
356 | ||
357 | filename_to_ansi (parent_dir, dir_a); | |
358 | if (*file) | |
359 | filename_to_ansi (file, file_a); | |
360 | else | |
361 | file_a[0] = '\0'; | |
362 | ||
363 | hdir = CreateFileA (dir_a, | |
364 | FILE_LIST_DIRECTORY, | |
365 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, | |
366 | NULL, OPEN_EXISTING, | |
367 | FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, | |
368 | NULL); | |
369 | } | |
6f011d81 EZ |
370 | if (hdir == INVALID_HANDLE_VALUE) |
371 | return NULL; | |
372 | ||
37a4dabe EZ |
373 | if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL) |
374 | CloseHandle (hdir); | |
6f011d81 | 375 | |
37a4dabe | 376 | return dirwatch; |
6f011d81 EZ |
377 | } |
378 | ||
37a4dabe | 379 | /* Stop watching a directory specified by a pointer to its dirwatch object. */ |
6f011d81 | 380 | static int |
37a4dabe | 381 | remove_watch (struct notification *dirwatch) |
6f011d81 | 382 | { |
37a4dabe | 383 | if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE) |
6f011d81 EZ |
384 | { |
385 | int i; | |
386 | BOOL status; | |
387 | DWORD exit_code, err; | |
388 | ||
389 | /* Only the thread that issued the outstanding I/O call can call | |
390 | CancelIo on it. (CancelIoEx is available only since Vista.) | |
391 | So we need to queue an APC for the worker thread telling it | |
392 | to terminate. */ | |
37a4dabe | 393 | if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir)) |
6f011d81 EZ |
394 | DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ())); |
395 | /* We also set the terminate flag, for when the thread is | |
396 | waiting on the critical section that never gets acquired. | |
397 | FIXME: is there a cleaner method? Using SleepEx there is a | |
398 | no-no, as that will lead to recursive APC invocations and | |
399 | stack overflow. */ | |
37a4dabe | 400 | dirwatch->terminate = 1; |
6f011d81 EZ |
401 | /* Wait for the thread to exit. FIXME: is there a better method |
402 | that is not overly complex? */ | |
403 | for (i = 0; i < 50; i++) | |
404 | { | |
37a4dabe | 405 | if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code)) |
6f011d81 EZ |
406 | && exit_code == STILL_ACTIVE)) |
407 | break; | |
408 | Sleep (10); | |
409 | } | |
410 | if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE) | |
411 | || exit_code == STILL_ACTIVE) | |
412 | { | |
413 | if (!(status == FALSE && err == ERROR_INVALID_HANDLE)) | |
37a4dabe EZ |
414 | { |
415 | TerminateThread (dirwatch->thr, 0); | |
416 | if (dirwatch->dir) | |
417 | CloseHandle (dirwatch->dir); | |
418 | } | |
6f011d81 EZ |
419 | } |
420 | ||
421 | /* Clean up. */ | |
37a4dabe | 422 | if (dirwatch->thr) |
6f011d81 | 423 | { |
37a4dabe EZ |
424 | CloseHandle (dirwatch->thr); |
425 | dirwatch->thr = NULL; | |
6f011d81 | 426 | } |
37a4dabe EZ |
427 | xfree (dirwatch->buf); |
428 | xfree (dirwatch->io_info); | |
429 | xfree (dirwatch->watchee); | |
430 | xfree (dirwatch); | |
431 | ||
6f011d81 EZ |
432 | return 0; |
433 | } | |
434 | else | |
435 | { | |
37a4dabe | 436 | DebPrint (("Unknown dirwatch object!\n")); |
6f011d81 EZ |
437 | return -1; |
438 | } | |
439 | } | |
440 | ||
441 | static DWORD | |
442 | filter_list_to_flags (Lisp_Object filter_list) | |
443 | { | |
444 | DWORD flags = 0; | |
445 | ||
446 | if (NILP (filter_list)) | |
447 | return flags; | |
448 | ||
449 | if (!NILP (Fmember (Qfile_name, filter_list))) | |
450 | flags |= FILE_NOTIFY_CHANGE_FILE_NAME; | |
451 | if (!NILP (Fmember (Qdirectory_name, filter_list))) | |
452 | flags |= FILE_NOTIFY_CHANGE_DIR_NAME; | |
453 | if (!NILP (Fmember (Qattributes, filter_list))) | |
454 | flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES; | |
455 | if (!NILP (Fmember (Qsize, filter_list))) | |
456 | flags |= FILE_NOTIFY_CHANGE_SIZE; | |
457 | if (!NILP (Fmember (Qlast_write_time, filter_list))) | |
458 | flags |= FILE_NOTIFY_CHANGE_LAST_WRITE; | |
459 | if (!NILP (Fmember (Qlast_access_time, filter_list))) | |
460 | flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS; | |
461 | if (!NILP (Fmember (Qcreation_time, filter_list))) | |
462 | flags |= FILE_NOTIFY_CHANGE_CREATION; | |
463 | if (!NILP (Fmember (Qsecurity_desc, filter_list))) | |
464 | flags |= FILE_NOTIFY_CHANGE_SECURITY; | |
465 | ||
466 | return flags; | |
467 | } | |
468 | ||
469 | DEFUN ("w32notify-add-watch", Fw32notify_add_watch, | |
470 | Sw32notify_add_watch, 3, 3, 0, | |
471 | doc: /* Add a watch for filesystem events pertaining to FILE. | |
472 | ||
473 | This arranges for filesystem events pertaining to FILE to be reported | |
474 | to Emacs. Use `w32notify-rm-watch' to cancel the watch. | |
475 | ||
d8cd7742 EZ |
476 | Value is a descriptor for the added watch. If the file cannot be |
477 | watched for some reason, this function signals a `file-error' error. | |
6f011d81 EZ |
478 | |
479 | FILTER is a list of conditions for reporting an event. It can include | |
480 | the following symbols: | |
481 | ||
482 | 'file-name' -- report file creation, deletion, or renaming | |
483 | 'directory-name' -- report directory creation, deletion, or renaming | |
484 | 'attributes' -- report changes in attributes | |
485 | 'size' -- report changes in file-size | |
486 | 'last-write-time' -- report changes in last-write time | |
487 | 'last-access-time' -- report changes in last-access time | |
488 | 'creation-time' -- report changes in creation time | |
489 | 'security-desc' -- report changes in security descriptor | |
490 | ||
491 | If FILE is a directory, and FILTER includes 'subtree', then all the | |
492 | subdirectories will also be watched and changes in them reported. | |
493 | ||
494 | When any event happens that satisfies the conditions specified by | |
495 | FILTER, Emacs will call the CALLBACK function passing it a single | |
496 | argument EVENT, which is of the form | |
497 | ||
498 | (DESCRIPTOR ACTION FILE) | |
499 | ||
500 | DESCRIPTOR is the same object as the one returned by this function. | |
501 | ACTION is the description of the event. It could be any one of the | |
502 | following: | |
503 | ||
504 | 'added' -- FILE was added | |
505 | 'removed' -- FILE was deleted | |
506 | 'modified' -- FILE's contents or its attributes were modified | |
507 | 'renamed-from' -- a file was renamed whose old name was FILE | |
508 | 'renamed-to' -- a file was renamed and its new name is FILE | |
509 | ||
d8cd7742 EZ |
510 | FILE is the name of the file whose event is being reported. |
511 | ||
512 | Note that some networked filesystems, such as Samba-mounted Unix | |
513 | volumes, might not send notifications about file changes. In these | |
514 | cases, this function will return a valid descriptor, but notifications | |
515 | will never come in. Volumes shared from remote Windows machines do | |
516 | generate notifications correctly, though. */) | |
6f011d81 EZ |
517 | (Lisp_Object file, Lisp_Object filter, Lisp_Object callback) |
518 | { | |
439b1ae8 | 519 | Lisp_Object dirfn, basefn, watch_object, watch_descriptor; |
6f011d81 EZ |
520 | DWORD flags; |
521 | BOOL subdirs = FALSE; | |
37a4dabe | 522 | struct notification *dirwatch = NULL; |
6f011d81 EZ |
523 | Lisp_Object lisp_errstr; |
524 | char *errstr; | |
525 | ||
526 | CHECK_LIST (filter); | |
527 | ||
528 | /* The underlying features are available only since XP. */ | |
529 | if (os_subtype == OS_9X | |
530 | || (w32_major_version == 5 && w32_major_version < 1)) | |
531 | { | |
532 | errno = ENOSYS; | |
533 | report_file_error ("Watching filesystem events is not supported", | |
534 | Qnil); | |
535 | } | |
536 | ||
fb6a5d68 EZ |
537 | /* filenotify.el always passes us a directory, either the parent |
538 | directory of a file to be watched, or the directory to be | |
539 | watched. */ | |
439b1ae8 EZ |
540 | file = Fdirectory_file_name (Fexpand_file_name (file, Qnil)); |
541 | if (NILP (Ffile_directory_p (file))) | |
fb6a5d68 EZ |
542 | { |
543 | /* This should only happen if we are called directly, not via | |
439b1ae8 EZ |
544 | filenotify.el. If BASEFN is empty, the argument was the root |
545 | directory on its drive. */ | |
546 | dirfn = ENCODE_FILE (Ffile_name_directory (file)); | |
547 | basefn = ENCODE_FILE (Ffile_name_nondirectory (file)); | |
548 | if (*SDATA (basefn) == '\0') | |
fb6a5d68 EZ |
549 | subdirs = TRUE; |
550 | } | |
439b1ae8 EZ |
551 | else |
552 | { | |
553 | dirfn = ENCODE_FILE (file); | |
554 | basefn = Qnil; | |
555 | } | |
6f011d81 EZ |
556 | |
557 | if (!NILP (Fmember (Qsubtree, filter))) | |
558 | subdirs = TRUE; | |
559 | ||
560 | flags = filter_list_to_flags (filter); | |
561 | ||
439b1ae8 EZ |
562 | dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn), |
563 | subdirs, flags); | |
37a4dabe | 564 | if (!dirwatch) |
6f011d81 EZ |
565 | { |
566 | DWORD err = GetLastError (); | |
567 | ||
568 | errno = EINVAL; | |
569 | if (err) | |
570 | { | |
571 | errstr = w32_strerror (err); | |
572 | if (!NILP (Vlocale_coding_system)) | |
573 | lisp_errstr | |
574 | = code_convert_string_norecord (build_unibyte_string (errstr), | |
575 | Vlocale_coding_system, 0); | |
576 | else | |
577 | lisp_errstr = build_string (errstr); | |
578 | report_file_error ("Cannot watch file", | |
579 | Fcons (lisp_errstr, Fcons (file, Qnil))); | |
580 | } | |
581 | else | |
582 | report_file_error ("Cannot watch file", Fcons (file, Qnil)); | |
583 | } | |
584 | /* Store watch object in watch list. */ | |
0b86d359 | 585 | watch_descriptor = XIL ((EMACS_INT)dirwatch); |
6f011d81 EZ |
586 | watch_object = Fcons (watch_descriptor, callback); |
587 | watch_list = Fcons (watch_object, watch_list); | |
588 | ||
589 | return watch_descriptor; | |
590 | } | |
591 | ||
592 | DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch, | |
593 | Sw32notify_rm_watch, 1, 1, 0, | |
594 | doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR. | |
595 | ||
596 | WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */) | |
597 | (Lisp_Object watch_descriptor) | |
598 | { | |
599 | Lisp_Object watch_object; | |
0b86d359 EZ |
600 | struct notification *dirwatch; |
601 | int status = -1; | |
6f011d81 | 602 | |
37a4dabe EZ |
603 | /* Remove the watch object from watch list. Do this before freeing |
604 | the object, do that even if we fail to free it, watch_list is | |
605 | kept free of junk. */ | |
6f011d81 EZ |
606 | watch_object = Fassoc (watch_descriptor, watch_list); |
607 | if (!NILP (watch_object)) | |
0b86d359 EZ |
608 | { |
609 | watch_list = Fdelete (watch_object, watch_list); | |
610 | dirwatch = (struct notification *)XLI (watch_descriptor); | |
611 | if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))) | |
612 | status = remove_watch (dirwatch); | |
613 | } | |
6f011d81 | 614 | |
0b86d359 | 615 | if (status == -1) |
37a4dabe EZ |
616 | report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor, |
617 | Qnil)); | |
618 | ||
6f011d81 EZ |
619 | return Qnil; |
620 | } | |
621 | ||
622 | Lisp_Object | |
0b86d359 | 623 | w32_get_watch_object (void *desc) |
37a4dabe | 624 | { |
0b86d359 EZ |
625 | Lisp_Object descriptor = XIL ((EMACS_INT)desc); |
626 | ||
d884121b EZ |
627 | /* This is called from the input queue handling code, inside a |
628 | critical section, so we cannot possibly QUIT if watch_list is not | |
629 | in the right condition. */ | |
0b86d359 | 630 | return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list); |
37a4dabe EZ |
631 | } |
632 | ||
633 | void | |
634 | globals_of_w32notify (void) | |
6f011d81 | 635 | { |
37a4dabe | 636 | watch_list = Qnil; |
6f011d81 EZ |
637 | } |
638 | ||
639 | void | |
640 | syms_of_w32notify (void) | |
641 | { | |
fe6aa7a1 BT |
642 | #include "w32notify.x" |
643 | ||
6f011d81 EZ |
644 | DEFSYM (Qfile_name, "file-name"); |
645 | DEFSYM (Qdirectory_name, "directory-name"); | |
646 | DEFSYM (Qattributes, "attributes"); | |
647 | DEFSYM (Qsize, "size"); | |
648 | DEFSYM (Qlast_write_time, "last-write-time"); | |
649 | DEFSYM (Qlast_access_time, "last-access-time"); | |
650 | DEFSYM (Qcreation_time, "creation-time"); | |
651 | DEFSYM (Qsecurity_desc, "security-desc"); | |
652 | DEFSYM (Qsubtree, "subtree"); | |
653 | ||
6f011d81 EZ |
654 | staticpro (&watch_list); |
655 | ||
656 | Fprovide (intern_c_string ("w32notify"), Qnil); | |
657 | } |