root/src/w32notify.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. send_notifications
  2. watch_end
  3. watch_completion
  4. watch_worker
  5. start_watching
  6. add_watch
  7. remove_watch
  8. filter_list_to_flags
  9. DEFUN
  10. w32_get_watch_object
  11. DEFUN
  12. globals_of_w32notify
  13. syms_of_w32notify

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

/* [<][>][^][v][top][bottom][index][help] */