* src/puresize.h (BASE_PURESIZE): Bump by another 1K.
[bpt/emacs.git] / src / w32notify.c
index afa0349..7155f16 100644 (file)
@@ -1,5 +1,5 @@
 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 2012  Free Software Foundation, Inc.
+   Copyright (C) 2012-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -16,15 +16,18 @@ 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/>.  */
 
-/* Design overview:
+/* Written by Eli Zaretskii <eliz@gnu.org>.
+
+   Design overview:
 
    For each watch request, we launch a separate worker thread.  The
    worker thread runs the watch_worker function, which issues an
-   asynchronous call to ReadDirectoryChangesW, and then waits for that
-   call to complete in SleepEx.  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.
+   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.
 
    When the ReadDirectoryChangesW call completes, its completion
    routine watch_completion is automatically called.  watch_completion
@@ -36,7 +39,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    return, and watch_worker then issues another call to
    ReadDirectoryChangesW.  (Except when it does not, see below.)
 
-   In a GUI session, The WM_EMACS_FILENOTIFY message, posted to the
+   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
@@ -56,12 +59,13 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
    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,
+   bound to a command.  The default binding is file-notify-handle-event,
    defined on subr.el.
 
-   After w32_read_socket or w32_console_read_socket is done processing
-   the notifications, it resets a flag signaling to all watch worker
-   threads that the notifications buffer is available for more input.
+   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.
 
    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
@@ -72,9 +76,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    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
-   ReadDirectoryChangesW call.  The main thread waits for some time
-   for the worker thread to exit, and if it doesn't, terminates it
-   forcibly.  */
+   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.  */
 
 #include <stddef.h>
 #include <errno.h>
@@ -100,7 +105,7 @@ struct notification {
   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 */
+  char *watchee;       /* the file we are interested in, UTF-8 encoded */
   HANDLE dir;          /* handle to the watched directory */
   HANDLE thr;          /* handle to the thread that watches */
   volatile int terminate; /* if non-zero, request for the thread to terminate */
@@ -124,7 +129,7 @@ send_notifications (BYTE *info, DWORD info_size, void *desc,
                    volatile int *terminate)
 {
   int done = 0;
-  FRAME_PTR f = SELECTED_FRAME ();
+  struct frame *f = SELECTED_FRAME ();
 
   /* A single buffer is used to communicate all notifications to the
      main thread.  Since both the main thread and several watcher
@@ -158,7 +163,12 @@ send_notifications (BYTE *info, DWORD info_size, void *desc,
               && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0))
              || (FRAME_W32_P (f)
                  && PostMessage (FRAME_W32_WINDOW (f),
-                                 WM_EMACS_FILENOTIFY, 0, 0)))
+                                 WM_EMACS_FILENOTIFY, 0, 0))
+             /* When we are running in batch mode, there's no one to
+                send a message, so we just signal the data is
+                available and hope sys_select will be called soon and
+                will read the data.  */
+             || (FRAME_INITIAL_P (f) && noninteractive))
            notification_buffer_in_use = 1;
          done = 1;
        }
@@ -185,9 +195,9 @@ watch_end (ULONG_PTR arg)
     }
 }
 
-/* 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
+/* 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
    "alertable state", i.e. waiting inside SleepEx call.  */
 VOID CALLBACK
 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
@@ -212,7 +222,7 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
       /* 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
+        and exit, signaling to the thread worker routine not to
         issue another call to ReadDirectoryChangesW.  Note that we
         don't free the dirwatch object itself nor the memory consumed
         by its buffers; this is done by the main thread in
@@ -319,18 +329,46 @@ add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
   HANDLE hdir;
   struct notification *dirwatch = NULL;
 
-  if (!file || !*file)
+  if (!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 (w32_unicode_filenames)
+    {
+      wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
+
+      filename_to_utf16 (parent_dir, dir_w);
+      if (*file)
+       filename_to_utf16 (file, file_w);
+      else
+       file_w[0] = 0;
+
+      hdir = CreateFileW (dir_w,
+                         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);
+    }
+  else
+    {
+      char dir_a[MAX_PATH], file_a[MAX_PATH];
+
+      filename_to_ansi (parent_dir, dir_a);
+      if (*file)
+       filename_to_ansi (file, file_a);
+      else
+       file_a[0] = '\0';
+
+      hdir = CreateFileA (dir_a,
+                         FILE_LIST_DIRECTORY,
+                         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;
 
@@ -437,8 +475,8 @@ DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
 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.
+Value is a descriptor for the added watch.  If the file cannot be
+watched for some reason, this function signals a `file-error' error.
 
 FILTER is a list of conditions for reporting an event.  It can include
 the following symbols:
@@ -471,12 +509,16 @@ following:
   '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.  */)
+FILE is the name of the file whose event is being reported.
+
+Note that some networked filesystems, such as Samba-mounted Unix
+volumes, might not send notifications about file changes.  In these
+cases, this function will return a valid descriptor, but notifications
+will never come in.  Volumes shared from remote Windows machines do
+generate notifications correctly, though.  */)
   (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;
+  Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
   DWORD flags;
   BOOL subdirs = FALSE;
   struct notification *dirwatch = NULL;
@@ -494,41 +536,33 @@ FILE is the name of the file whose event is being reported.  */)
                         Qnil);
     }
 
-  /* We need a full absolute file name of FILE, and we need to remove
-     any trailing slashes from it, so that GetFullPathName below gets
-     the basename part correctly.  */
+  /* filenotify.el always passes us a directory, either the parent
+     directory of a file to be watched, or the directory to be
+     watched.  */
   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)
+  if (NILP (Ffile_directory_p (file)))
     {
-      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)));
+      /* This should only happen if we are called directly, not via
+        filenotify.el.  If BASEFN is empty, the argument was the root
+        directory on its drive.  */
+      dirfn = ENCODE_FILE (Ffile_name_directory (file));
+      basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
+      if (*SDATA (basefn) == '\0')
+       subdirs = TRUE;
     }
-  /* 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;
+    {
+      dirfn = ENCODE_FILE (file);
+      basefn = Qnil;
+    }
 
   if (!NILP (Fmember (Qsubtree, filter)))
     subdirs = TRUE;
 
   flags = filter_list_to_flags (filter);
 
-  dirwatch = add_watch (parent_dir, basename, subdirs, flags);
+  dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
+                       subdirs, flags);
   if (!dirwatch)
     {
       DWORD err = GetLastError ();