Subject: bug#19909: [PATCH] Error out if with-file-notification=w32 is specified on cygwin

From: Ken Brown <[email-masked]>
Date: Fri, 27 Feb 2015 17:25:54 -0500

On 2/20/2015 12:18 PM, Eli Zaretskii wrote:
>> Date: Fri, 20 Feb 2015 11:15:18 -0500
>> From: Ken Brown <[email-masked]>
>> CC: [email-masked], Robert Pluim <[email-masked]>
>>
>> I'm busy with other things at the moment and can't get to it right away.
>>    If it's easy for you to suggest a patch, why don't you go ahead, and
>> Robert can test it.  Or you can wait a couple weeks until I can look at it.
>
> OK, I will try to find time.
>
>> BTW, file-notification=gfile works fine in the Cygwin-w32 build, so I'm
>> not sure there's any need to put a lot of effort into this.
>
> If it turns out to need a lot of effort, I'll agree with you.

It looks like it might not take much effort.  What I did for now, just 
for testing purposes, is to copy into w32notify.c whatever was necessary 
to make the Cygwin-w32 build compile with w32 file notification.  I 
tested it briefly and it seems to work.  Obviously it needs much more 
testing to make sure this doesn't break anything else, and then we can 
worry about how to do this right.

Robert, please test when you get a chance (patch attached).

Ken
diff --git a/src/w32notify.c b/src/w32notify.c
index ab6cd12..71ee1a3 100644
--- a/src/w32notify.c
+++ b/src/w32notify.c
@@ -92,12 +92,216 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "lisp.h"
 #include "w32term.h"	/* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
 #include "w32common.h"	/* for OS version data */
-#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 */
 
+/* Experimental code for Cygwin-w32 build */
+
+extern char * w32_strerror (int error_no);
+
+int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int);
+int (WINAPI *pWideCharToMultiByte)(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL);
+
+#define MAXPATHLEN      MAX_PATH
+#define MAX_UTF8_PATH   (MAXPATHLEN * 4)
+
+static int file_name_codepage;
+
+static int
+codepage_for_filenames (CPINFO *cp_info)
+{
+  /* A simple cache to avoid calling GetCPInfo every time we need to
+     encode/decode a file name.  The file-name encoding is not
+     supposed to be changed too frequently, if ever.  */
+  static Lisp_Object last_file_name_encoding;
+  static CPINFO cp;
+  Lisp_Object current_encoding;
+
+  current_encoding = Vfile_name_coding_system;
+  if (NILP (current_encoding))
+    current_encoding = Vdefault_file_name_coding_system;
+
+  if (!EQ (last_file_name_encoding, current_encoding))
+    {
+      /* Default to the current ANSI codepage.  */
+      file_name_codepage = w32_ansi_code_page;
+
+      if (NILP (current_encoding))
+	{
+	  char *cpname = SDATA (SYMBOL_NAME (current_encoding));
+	  char *cp = NULL, *end;
+	  int cpnum;
+
+	  if (strncmp (cpname, "cp", 2) == 0)
+	    cp = cpname + 2;
+	  else if (strncmp (cpname, "windows-", 8) == 0)
+	    cp = cpname + 8;
+
+	  if (cp)
+	    {
+	      end = cp;
+	      cpnum = strtol (cp, &end, 10);
+	      if (cpnum && *end == '\0' && end - cp >= 2)
+		file_name_codepage = cpnum;
+	    }
+	}
+
+      if (!file_name_codepage)
+	file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
+
+      if (!GetCPInfo (file_name_codepage, &cp))
+	{
+	  file_name_codepage = CP_ACP;
+	  if (!GetCPInfo (file_name_codepage, &cp))
+	    emacs_abort ();
+	}
+    }
+  if (cp_info)
+    *cp_info = cp;
+
+  return file_name_codepage;
+}
+
+int
+filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
+{
+  int result = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
+				     fn_out, MAX_PATH);
+
+  if (!result)
+    {
+      DWORD err = GetLastError ();
+
+      switch (err)
+	{
+	case ERROR_INVALID_FLAGS:
+	case ERROR_INVALID_PARAMETER:
+	  errno = EINVAL;
+	  break;
+	case ERROR_INSUFFICIENT_BUFFER:
+	case ERROR_NO_UNICODE_TRANSLATION:
+	default:
+	  errno = ENOENT;
+	  break;
+	}
+      return -1;
+    }
+  return 0;
+}
+
+int
+filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
+{
+  int result = pWideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
+				     fn_out, MAX_UTF8_PATH, NULL, NULL);
+
+  if (!result)
+    {
+      DWORD err = GetLastError ();
+
+      switch (err)
+	{
+	case ERROR_INVALID_FLAGS:
+	case ERROR_INVALID_PARAMETER:
+	  errno = EINVAL;
+	  break;
+	case ERROR_INSUFFICIENT_BUFFER:
+	case ERROR_NO_UNICODE_TRANSLATION:
+	default:
+	  errno = ENOENT;
+	  break;
+	}
+      return -1;
+    }
+  return 0;
+}
+
+int
+filename_to_ansi (const char *fn_in, char *fn_out)
+{
+  wchar_t fn_utf16[MAX_PATH];
+
+  if (filename_to_utf16 (fn_in, fn_utf16) == 0)
+    {
+      int result;
+      int codepage = codepage_for_filenames (NULL);
+
+      result  = pWideCharToMultiByte (codepage, 0, fn_utf16, -1,
+				      fn_out, MAX_PATH, NULL, NULL);
+      if (!result)
+	{
+	  DWORD err = GetLastError ();
+
+	  switch (err)
+	    {
+	    case ERROR_INVALID_FLAGS:
+	    case ERROR_INVALID_PARAMETER:
+	      errno = EINVAL;
+	      break;
+	    case ERROR_INSUFFICIENT_BUFFER:
+	    case ERROR_NO_UNICODE_TRANSLATION:
+	    default:
+	      errno = ENOENT;
+	      break;
+	    }
+	  return -1;
+	}
+      return 0;
+    }
+  return -1;
+}
+
+int
+filename_from_ansi (const char *fn_in, char *fn_out)
+{
+  wchar_t fn_utf16[MAX_PATH];
+  int codepage = codepage_for_filenames (NULL);
+  int result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
+				     fn_utf16, MAX_PATH);
+
+  if (!result)
+    {
+      DWORD err = GetLastError ();
+
+      switch (err)
+	{
+	case ERROR_INVALID_FLAGS:
+	case ERROR_INVALID_PARAMETER:
+	  errno = EINVAL;
+	  break;
+	case ERROR_INSUFFICIENT_BUFFER:
+	case ERROR_NO_UNICODE_TRANSLATION:
+	default:
+	  errno = ENOENT;
+	  break;
+	}
+      return -1;
+    }
+  return filename_from_utf16 (fn_utf16, fn_out);
+}
+
+int
+w32_valid_pointer_p (void *p, int size)
+{
+  SIZE_T done;
+  HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
+
+  if (h)
+    {
+      unsigned char *buf = alloca (size);
+      int retval = ReadProcessMemory (h, p, buf, size, &done);
+
+      CloseHandle (h);
+      return retval;
+    }
+  else
+    return -1;
+}
+
+/* End experimental code for Cygwin-w32 build */
+
 #define DIRWATCH_SIGNATURE 0x01233210
 
 struct notification {
diff --git a/src/w32term.c b/src/w32term.c
index d415b13..061c9c3 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -3195,7 +3195,6 @@ lispy_file_action (DWORD action)
   return retval;
 }
 
-#ifdef WINDOWSNT
 /* Put file notifications into the Emacs input event queue.  This
    function runs when the WM_EMACS_FILENOTIFY message arrives from a
    watcher thread.  */
@@ -3271,7 +3270,6 @@ queue_notifications (struct input_event *event, W32Msg *msg, struct frame *f,
   /* We've stuffed all the events ourselves, so w32_read_socket shouldn't.  */
   event->kind = NO_EVENT;
 }
-#endif	/* WINDOWSNT */
 #endif	/* HAVE_W32NOTIFY */