w32notify.c 21.9 KB
Newer Older
Eli Zaretskii's avatar
Eli Zaretskii committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
   Copyright (C) 2012  Free Software Foundation, Inc.

This file is part of GNU Emacs.

GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

19 20 21
/* Written by Eli Zaretskii <eliz@gnu.org>.

   Design overview:
22 23 24

   For each watch request, we launch a separate worker thread.  The
   worker thread runs the watch_worker function, which issues an
25 26 27 28 29 30
   asynchronous call to ReadDirectoryChangesW, and then waits in
   SleepEx for that call to complete.  Waiting in SleepEx puts the
   thread in an "alertable" state, so it wakes up when either (a) the
   call to ReadDirectoryChangesW completes, or (b) the main thread
   instructs the worker thread to terminate by sending it an APC, see
   below.
31 32 33 34 35 36 37 38 39 40 41

   When the ReadDirectoryChangesW call completes, its completion
   routine watch_completion is automatically called.  watch_completion
   stashes the received file events in a buffer used to communicate
   them to the main thread (using a critical section, so that several
   threads could use the same buffer), posts a special message,
   WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns.
   That causes the SleepEx function call inside watch_worker to
   return, and watch_worker then issues another call to
   ReadDirectoryChangesW.  (Except when it does not, see below.)

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
   In a GUI session, The WM_EMACS_FILENOTIFY message, posted to the
   message queue gets dispatched to the main Emacs window procedure,
   which queues it for processing by w32_read_socket.  When
   w32_read_socket sees this message, it accesses the buffer with file
   notifications (using a critical section), extracts the information,
   converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
   them into the input event queue to be processed by keyboard.c input
   machinery (read_char via a call to kbd_buffer_get_event).

   In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
   the main (a.k.a. "Lisp") thread instead, since there are no window
   procedures in console programs.  That message wakes up
   MsgWaitForMultipleObjects inside sys_select, which then signals to
   its caller that some keyboard input is available.  This causes
   w32_console_read_socket to be called, which accesses the buffer
   with file notifications and stuffs them into the input event queue
   for keyboard.c to process.

   When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
   kbd_buffer_get_event, it is converted to a Lispy event that can be
   bound to a command.  The default binding is w32notify-handle-event,
   defined on subr.el.

65 66 67 68
   After w32_read_socket or w32_console_read_socket are done
   processing the notifications, they reset a flag signaling to all
   watch worker threads that the notifications buffer is available for
   more input.
69 70 71 72 73 74 75 76 77 78

   When the watch is removed by a call to w32notify-rm-watch, the main
   thread requests that the worker thread terminates by queuing an APC
   for the worker thread.  The APC specifies the watch_end function to
   be called.  watch_end calls CancelIo on the outstanding
   ReadDirectoryChangesW call and closes the handle on which the
   watched directory was open.  When watch_end returns, the
   watch_completion function is called one last time with the
   ERROR_OPERATION_ABORTED status, which causes it to clean up and set
   a flag telling watch_worker to exit without issuing another
79 80 81 82
   ReadDirectoryChangesW call.  Since watch_worker is the thread
   procedure of the worker thread, exiting it causes the thread to
   exit.  The main thread waits for some time for the worker thread to
   exit, and if it doesn't, terminates it forcibly.  */
83

Eli Zaretskii's avatar
Eli Zaretskii committed
84 85 86 87 88 89 90 91 92 93
#include <stddef.h>
#include <errno.h>

/* must include CRT headers *before* config.h */
#include <config.h>

#include <windows.h>

#include "lisp.h"
#include "w32term.h"	/* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
94
#include "w32common.h"	/* for OS version data */
Eli Zaretskii's avatar
Eli Zaretskii committed
95 96 97 98 99 100
#include "w32.h"	/* for w32_strerror */
#include "coding.h"
#include "keyboard.h"
#include "frame.h"	/* needed by termhooks.h */
#include "termhooks.h"	/* for FILE_NOTIFY_EVENT */

101 102
#define DIRWATCH_SIGNATURE 0x01233210

Eli Zaretskii's avatar
Eli Zaretskii committed
103 104 105 106 107 108 109 110
struct notification {
  BYTE *buf;		/* buffer for ReadDirectoryChangesW */
  OVERLAPPED *io_info;	/* the OVERLAPPED structure for async I/O */
  BOOL subtree;		/* whether to watch subdirectories */
  DWORD filter;		/* bit mask for events to watch */
  char *watchee;	/* the file we are interested in */
  HANDLE dir;		/* handle to the watched directory */
  HANDLE thr;		/* handle to the thread that watches */
111
  volatile int terminate; /* if non-zero, request for the thread to terminate */
112
  unsigned signature;
Eli Zaretskii's avatar
Eli Zaretskii committed
113 114 115
};

/* Used for communicating notifications to the main thread.  */
116
volatile int notification_buffer_in_use;
Eli Zaretskii's avatar
Eli Zaretskii committed
117 118
BYTE file_notifications[16384];
DWORD notifications_size;
119
void *notifications_desc;
Eli Zaretskii's avatar
Eli Zaretskii committed
120 121 122 123 124 125 126 127

static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;

/* Signal to the main thread that we have file notifications for it to
   process.  */
static void
128 129
send_notifications (BYTE *info, DWORD info_size, void *desc,
		    volatile int *terminate)
Eli Zaretskii's avatar
Eli Zaretskii committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
{
  int done = 0;
  FRAME_PTR f = SELECTED_FRAME ();

  /* A single buffer is used to communicate all notifications to the
     main thread.  Since both the main thread and several watcher
     threads could be active at the same time, we use a critical area
     and an "in-use" flag to synchronize them.  A watcher thread can
     only put its notifications in the buffer if it acquires the
     critical area and finds the "in-use" flag reset.  The main thread
     resets the flag after it is done processing notifications.

     FIXME: is there a better way of dealing with this?  */
  while (!done && !*terminate)
    {
      enter_crit ();
      if (!notification_buffer_in_use)
	{
	  if (info_size)
	    memcpy (file_notifications, info, info_size);
	  notifications_size = info_size;
151
	  notifications_desc = desc;
152 153 154 155 156 157 158 159 160 161 162 163
	  /* If PostMessage fails, the message queue is full.  If that
	     happens, the last thing they will worry about is file
	     notifications.  So we effectively discard the
	     notification in that case.  */
	  if ((FRAME_TERMCAP_P (f)
	       /* We send the message to the main (a.k.a. "Lisp")
		  thread, where it will wake up MsgWaitForMultipleObjects
		  inside sys_select, causing it to report that there's
		  some keyboard input available.  This will in turn cause
		  w32_console_read_socket to be called, which will pick
		  up the file notifications.  */
	       && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0))
164 165 166
	      || (FRAME_W32_P (f)
		  && PostMessage (FRAME_W32_WINDOW (f),
				  WM_EMACS_FILENOTIFY, 0, 0)))
Eli Zaretskii's avatar
Eli Zaretskii committed
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
	    notification_buffer_in_use = 1;
	  done = 1;
	}
      leave_crit ();
      if (!done)
	Sleep (5);
    }
}

/* An APC routine to cancel outstanding directory watch.  Invoked by
   the main thread via QueueUserAPC.  This is needed because only the
   thread that issued the ReadDirectoryChangesW call can call CancelIo
   to cancel that.  (CancelIoEx is only available since Vista, so we
   cannot use it on XP.)  */
VOID CALLBACK
watch_end (ULONG_PTR arg)
{
  HANDLE hdir = (HANDLE)arg;

  if (hdir && hdir != INVALID_HANDLE_VALUE)
    {
      CancelIo (hdir);
      CloseHandle (hdir);
    }
}

193 194 195
/* A completion routine (a.k.a. "APC function") for handling events
   read by ReadDirectoryChangesW.  Called by the OS when the thread
   which issued the asynchronous ReadDirectoryChangesW call is in the
Eli Zaretskii's avatar
Eli Zaretskii committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
   "alertable state", i.e. waiting inside SleepEx call.  */
VOID CALLBACK
watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
{
  struct notification *dirwatch;

  /* Who knows what happened?  Perhaps the OVERLAPPED structure was
     freed by someone already?  In any case, we cannot do anything
     with this request, so just punt and skip it.  FIXME: should we
     raise the 'terminate' flag in this case?  */
  if (!io_info)
    return;

  /* We have a pointer to our dirwatch structure conveniently stashed
     away in the hEvent member of the OVERLAPPED struct.  According to
     MSDN documentation of ReadDirectoryChangesW: "The hEvent member
     of the OVERLAPPED structure is not used by the system, so you can
     use it yourself."  */
  dirwatch = (struct notification *)io_info->hEvent;
  if (status == ERROR_OPERATION_ABORTED)
    {
      /* We've been called because the main thread told us to issue
	 CancelIo on the directory we watch, and watch_end did so.
	 The directory handle is already closed.  We should clean up
	 and exit, signalling to the thread worker routine not to
221
	 issue another call to ReadDirectoryChangesW.  Note that we
222 223 224 225
	 don't free the dirwatch object itself nor the memory consumed
	 by its buffers; this is done by the main thread in
	 remove_watch.  Calling malloc/free from a thread other than
	 the main thread is a no-no.  */
226
      dirwatch->dir = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
227 228 229 230 231
      dirwatch->terminate = 1;
    }
  else
    {
      /* Tell the main thread we have notifications for it.  */
232
      send_notifications (dirwatch->buf, bytes_ret, dirwatch,
Eli Zaretskii's avatar
Eli Zaretskii committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
			  &dirwatch->terminate);
    }
}

/* Worker routine for the watch thread.  */
static DWORD WINAPI
watch_worker (LPVOID arg)
{
  struct notification *dirwatch = (struct notification *)arg;

  do {
    BOOL status;
    DWORD sleep_result;
    DWORD bytes_ret = 0;

    if (dirwatch->dir)
      {
	status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
					dirwatch->subtree, dirwatch->filter,
					&bytes_ret,
					dirwatch->io_info, watch_completion);
	if (!status)
	  {
256
	    DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
257
	    /* We cannot remove the dirwatch object from watch_list,
258 259 260 261 262 263 264
	       because we are in a separate thread.  For the same
	       reason, we also cannot free memory consumed by the
	       buffers allocated for the dirwatch object.  So we close
	       the directory handle, but do not free the object itself
	       or its buffers.  We also don't touch the signature.
	       This way, remove_watch can still identify the object,
	       remove it, and free its memory.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
	    CloseHandle (dirwatch->dir);
	    dirwatch->dir = NULL;
	    return 1;
	  }
      }
    /* Sleep indefinitely until awoken by the I/O completion, which
       could be either a change notification or a cancellation of the
       watch.  */
    sleep_result = SleepEx (INFINITE, TRUE);
  } while (!dirwatch->terminate);

  return 0;
}

/* Launch a thread to watch changes to FILE in a directory open on
   handle HDIR.  */
281
static struct notification *
Eli Zaretskii's avatar
Eli Zaretskii committed
282 283
start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
{
284 285 286 287 288 289
  struct notification *dirwatch = xzalloc (sizeof (struct notification));
  HANDLE thr;

  dirwatch->signature = DIRWATCH_SIGNATURE;
  dirwatch->buf = xmalloc (16384);
  dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
Eli Zaretskii's avatar
Eli Zaretskii committed
290 291 292 293
  /* Stash a pointer to dirwatch structure for use by the completion
     routine.  According to MSDN documentation of ReadDirectoryChangesW:
     "The hEvent member of the OVERLAPPED structure is not used by the
     system, so you can use it yourself." */
294 295 296 297 298 299
  dirwatch->io_info->hEvent = dirwatch;
  dirwatch->subtree = subdirs;
  dirwatch->filter = flags;
  dirwatch->watchee = xstrdup (file);
  dirwatch->terminate = 0;
  dirwatch->dir = hdir;
Eli Zaretskii's avatar
Eli Zaretskii committed
300 301 302

  /* See w32proc.c where it calls CreateThread for the story behind
     the 2nd and 5th argument in the call to CreateThread.  */
303 304
  dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
				0x00010000, NULL);
Eli Zaretskii's avatar
Eli Zaretskii committed
305

306
  if (!dirwatch->thr)
Eli Zaretskii's avatar
Eli Zaretskii committed
307
    {
308 309 310 311 312
      xfree (dirwatch->buf);
      xfree (dirwatch->io_info);
      xfree (dirwatch->watchee);
      xfree (dirwatch);
      dirwatch = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
313
    }
314
  return dirwatch;
Eli Zaretskii's avatar
Eli Zaretskii committed
315 316 317 318
}

/* Called from the main thread to start watching FILE in PARENT_DIR,
   subject to FLAGS.  If SUBDIRS is TRUE, watch the subdirectories of
319 320 321
   PARENT_DIR as well.  Value is a pointer to 'struct notification'
   used by the thread that watches the changes.  */
static struct notification *
Eli Zaretskii's avatar
Eli Zaretskii committed
322 323 324
add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
{
  HANDLE hdir;
325
  struct notification *dirwatch = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341

  if (!file || !*file)
    return NULL;

  hdir = CreateFile (parent_dir,
		     FILE_LIST_DIRECTORY,
		     /* FILE_SHARE_DELETE doesn't preclude other
			processes from deleting files inside
			parent_dir.  */
		     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		     NULL, OPEN_EXISTING,
		     FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
		     NULL);
  if (hdir == INVALID_HANDLE_VALUE)
    return NULL;

342 343
  if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
    CloseHandle (hdir);
Eli Zaretskii's avatar
Eli Zaretskii committed
344

345
  return dirwatch;
Eli Zaretskii's avatar
Eli Zaretskii committed
346 347
}

348
/* Stop watching a directory specified by a pointer to its dirwatch object.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
349
static int
350
remove_watch (struct notification *dirwatch)
Eli Zaretskii's avatar
Eli Zaretskii committed
351
{
352
  if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
Eli Zaretskii's avatar
Eli Zaretskii committed
353 354 355 356 357 358 359 360 361
    {
      int i;
      BOOL status;
      DWORD exit_code, err;

      /* Only the thread that issued the outstanding I/O call can call
	 CancelIo on it.  (CancelIoEx is available only since Vista.)
	 So we need to queue an APC for the worker thread telling it
	 to terminate.  */
362
      if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
Eli Zaretskii's avatar
Eli Zaretskii committed
363 364 365 366 367 368
	DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
      /* We also set the terminate flag, for when the thread is
	 waiting on the critical section that never gets acquired.
	 FIXME: is there a cleaner method?  Using SleepEx there is a
	 no-no, as that will lead to recursive APC invocations and
	 stack overflow.  */
369
      dirwatch->terminate = 1;
Eli Zaretskii's avatar
Eli Zaretskii committed
370 371 372 373
      /* Wait for the thread to exit.  FIXME: is there a better method
	 that is not overly complex?  */
      for (i = 0; i < 50; i++)
	{
374
	  if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
Eli Zaretskii's avatar
Eli Zaretskii committed
375 376 377 378 379 380 381 382
		&& exit_code == STILL_ACTIVE))
	    break;
	  Sleep (10);
	}
      if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
	  || exit_code == STILL_ACTIVE)
	{
	  if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
383 384 385 386 387
	    {
	      TerminateThread (dirwatch->thr, 0);
	      if (dirwatch->dir)
		CloseHandle (dirwatch->dir);
	    }
Eli Zaretskii's avatar
Eli Zaretskii committed
388 389 390
	}

      /* Clean up.  */
391
      if (dirwatch->thr)
Eli Zaretskii's avatar
Eli Zaretskii committed
392
	{
393 394
	  CloseHandle (dirwatch->thr);
	  dirwatch->thr = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
395
	}
396 397 398 399 400
      xfree (dirwatch->buf);
      xfree (dirwatch->io_info);
      xfree (dirwatch->watchee);
      xfree (dirwatch);

Eli Zaretskii's avatar
Eli Zaretskii committed
401 402 403 404
      return 0;
    }
  else
    {
405
      DebPrint (("Unknown dirwatch object!\n"));
Eli Zaretskii's avatar
Eli Zaretskii committed
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
      return -1;
    }
}

static DWORD
filter_list_to_flags (Lisp_Object filter_list)
{
  DWORD flags = 0;

  if (NILP (filter_list))
    return flags;

  if (!NILP (Fmember (Qfile_name, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
  if (!NILP (Fmember (Qdirectory_name, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
  if (!NILP (Fmember (Qattributes, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
  if (!NILP (Fmember (Qsize, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_SIZE;
  if (!NILP (Fmember (Qlast_write_time, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
  if (!NILP (Fmember (Qlast_access_time, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
  if (!NILP (Fmember (Qcreation_time, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_CREATION;
  if (!NILP (Fmember (Qsecurity_desc, filter_list)))
    flags |= FILE_NOTIFY_CHANGE_SECURITY;

  return flags;
}

DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
       Sw32notify_add_watch, 3, 3, 0,
       doc: /* Add a watch for filesystem events pertaining to FILE.

This arranges for filesystem events pertaining to FILE to be reported
to Emacs.  Use `w32notify-rm-watch' to cancel the watch.

Value is a descriptor for the added watch, or nil if the file
cannot be watched.

FILTER is a list of conditions for reporting an event.  It can include
the following symbols:

  'file-name'          -- report file creation, deletion, or renaming
  'directory-name'     -- report directory creation, deletion, or renaming
  'attributes'         -- report changes in attributes
  'size'               -- report changes in file-size
  'last-write-time'    -- report changes in last-write time
  'last-access-time'   -- report changes in last-access time
  'creation-time'      -- report changes in creation time
  'security-desc'      -- report changes in security descriptor

If FILE is a directory, and FILTER includes 'subtree', then all the
subdirectories will also be watched and changes in them reported.

When any event happens that satisfies the conditions specified by
FILTER, Emacs will call the CALLBACK function passing it a single
argument EVENT, which is of the form

  (DESCRIPTOR ACTION FILE)

DESCRIPTOR is the same object as the one returned by this function.
ACTION is the description of the event.  It could be any one of the
following:

  'added'        -- FILE was added
  'removed'      -- FILE was deleted
  'modified'     -- FILE's contents or its attributes were modified
  'renamed-from' -- a file was renamed whose old name was FILE
  'renamed-to'   -- a file was renamed and its new name is FILE

FILE is the name of the file whose event is being reported.  */)
  (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
{
  Lisp_Object encoded_file, watch_object, watch_descriptor;
  char parent_dir[MAX_PATH], *basename;
  size_t fn_len;
  DWORD flags;
  BOOL subdirs = FALSE;
487
  struct notification *dirwatch = NULL;
Eli Zaretskii's avatar
Eli Zaretskii committed
488 489 490 491 492 493 494 495 496 497 498 499 500 501
  Lisp_Object lisp_errstr;
  char *errstr;

  CHECK_LIST (filter);

  /* The underlying features are available only since XP.  */
  if (os_subtype == OS_9X
      || (w32_major_version == 5 && w32_major_version < 1))
    {
      errno = ENOSYS;
      report_file_error ("Watching filesystem events is not supported",
			 Qnil);
    }

502
  /* We need a full absolute file name of FILE, and we need to remove
Eli Zaretskii's avatar
Eli Zaretskii committed
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
     any trailing slashes from it, so that GetFullPathName below gets
     the basename part correctly.  */
  file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
  encoded_file = ENCODE_FILE (file);

  fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
			    &basename);
  if (!fn_len)
    {
      errstr = w32_strerror (0);
      errno = EINVAL;
      if (!NILP (Vlocale_coding_system))
	lisp_errstr
	  = code_convert_string_norecord (build_unibyte_string (errstr),
					  Vlocale_coding_system, 0);
      else
	lisp_errstr = build_string (errstr);
      report_file_error ("GetFullPathName failed",
			 Fcons (lisp_errstr, Fcons (file, Qnil)));
    }
  /* We need the parent directory without the slash that follows it.
     If BASENAME is NULL, the argument was the root directory on its
     drive.  */
  if (basename)
    basename[-1] = '\0';
  else
    subdirs = TRUE;

  if (!NILP (Fmember (Qsubtree, filter)))
    subdirs = TRUE;

  flags = filter_list_to_flags (filter);

536 537
  dirwatch = add_watch (parent_dir, basename, subdirs, flags);
  if (!dirwatch)
Eli Zaretskii's avatar
Eli Zaretskii committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    {
      DWORD err = GetLastError ();

      errno = EINVAL;
      if (err)
	{
	  errstr = w32_strerror (err);
	  if (!NILP (Vlocale_coding_system))
	    lisp_errstr
	      = code_convert_string_norecord (build_unibyte_string (errstr),
					      Vlocale_coding_system, 0);
	  else
	    lisp_errstr = build_string (errstr);
	  report_file_error ("Cannot watch file",
			     Fcons (lisp_errstr, Fcons (file, Qnil)));
	}
      else
	report_file_error ("Cannot watch file", Fcons (file, Qnil));
    }
  /* Store watch object in watch list. */
558
  watch_descriptor = XIL ((EMACS_INT)dirwatch);
Eli Zaretskii's avatar
Eli Zaretskii committed
559 560 561 562 563 564 565 566 567 568 569 570 571 572
  watch_object = Fcons (watch_descriptor, callback);
  watch_list = Fcons (watch_object, watch_list);

  return watch_descriptor;
}

DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
       Sw32notify_rm_watch, 1, 1, 0,
       doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.

WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.  */)
     (Lisp_Object watch_descriptor)
{
  Lisp_Object watch_object;
573 574
  struct notification *dirwatch;
  int status = -1;
Eli Zaretskii's avatar
Eli Zaretskii committed
575

576 577 578
  /* Remove the watch object from watch list.  Do this before freeing
     the object, do that even if we fail to free it, watch_list is
     kept free of junk.  */
Eli Zaretskii's avatar
Eli Zaretskii committed
579 580
  watch_object = Fassoc (watch_descriptor, watch_list);
  if (!NILP (watch_object))
581 582 583 584 585 586
    {
      watch_list = Fdelete (watch_object, watch_list);
      dirwatch = (struct notification *)XLI (watch_descriptor);
      if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
	status = remove_watch (dirwatch);
    }
Eli Zaretskii's avatar
Eli Zaretskii committed
587

588
  if (status == -1)
589 590 591
    report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
							  Qnil));

Eli Zaretskii's avatar
Eli Zaretskii committed
592 593 594 595
  return Qnil;
}

Lisp_Object
596
w32_get_watch_object (void *desc)
597
{
598 599
  Lisp_Object descriptor = XIL ((EMACS_INT)desc);

600 601 602
  /* This is called from the input queue handling code, inside a
     critical section, so we cannot possibly QUIT if watch_list is not
     in the right condition.  */
603
  return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
604 605 606 607
}

void
globals_of_w32notify (void)
Eli Zaretskii's avatar
Eli Zaretskii committed
608
{
609
  watch_list = Qnil;
Eli Zaretskii's avatar
Eli Zaretskii committed
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
}

void
syms_of_w32notify (void)
{
  DEFSYM (Qfile_name, "file-name");
  DEFSYM (Qdirectory_name, "directory-name");
  DEFSYM (Qattributes, "attributes");
  DEFSYM (Qsize, "size");
  DEFSYM (Qlast_write_time, "last-write-time");
  DEFSYM (Qlast_access_time, "last-access-time");
  DEFSYM (Qcreation_time, "creation-time");
  DEFSYM (Qsecurity_desc, "security-desc");
  DEFSYM (Qsubtree, "subtree");

  defsubr (&Sw32notify_add_watch);
  defsubr (&Sw32notify_rm_watch);

  staticpro (&watch_list);

  Fprovide (intern_c_string ("w32notify"), Qnil);
}