plist module
[bpt/emacs.git] / src / w32.c
index f593831..76b12ad 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,5 +1,6 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
+
+Copyright (C) 1994-1995, 2000-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -72,9 +73,11 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <grp.h>
 
 /* MinGW64 (_W64) defines these in its _mingw.h.  */
-#if defined(__GNUC__) && !defined(_W64)
-#define _ANONYMOUS_UNION
-#define _ANONYMOUS_STRUCT
+#ifndef _ANONYMOUS_UNION
+# define _ANONYMOUS_UNION
+#endif
+#ifndef _ANONYMOUS_STRUCT
+# define _ANONYMOUS_STRUCT
 #endif
 #include <windows.h>
 /* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
@@ -140,6 +143,7 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX {
 #include <sddl.h>
 
 #include <sys/acl.h>
+#include <acl.h>
 
 /* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
    define them by hand if not already defined.  */
@@ -273,7 +277,8 @@ static BOOL g_b_init_lookup_account_sid;
 static BOOL g_b_init_get_sid_sub_authority;
 static BOOL g_b_init_get_sid_sub_authority_count;
 static BOOL g_b_init_get_security_info;
-static BOOL g_b_init_get_file_security;
+static BOOL g_b_init_get_file_security_w;
+static BOOL g_b_init_get_file_security_a;
 static BOOL g_b_init_get_security_descriptor_owner;
 static BOOL g_b_init_get_security_descriptor_group;
 static BOOL g_b_init_is_valid_sid;
@@ -292,12 +297,16 @@ static BOOL g_b_init_equal_sid;
 static BOOL g_b_init_copy_sid;
 static BOOL g_b_init_get_native_system_info;
 static BOOL g_b_init_get_system_times;
-static BOOL g_b_init_create_symbolic_link;
+static BOOL g_b_init_create_symbolic_link_w;
+static BOOL g_b_init_create_symbolic_link_a;
 static BOOL g_b_init_get_security_descriptor_dacl;
 static BOOL g_b_init_convert_sd_to_sddl;
 static BOOL g_b_init_convert_sddl_to_sd;
 static BOOL g_b_init_is_valid_security_descriptor;
-static BOOL g_b_init_set_file_security;
+static BOOL g_b_init_set_file_security_w;
+static BOOL g_b_init_set_file_security_a;
+static BOOL g_b_init_set_named_security_info_w;
+static BOOL g_b_init_set_named_security_info_a;
 static BOOL g_b_init_get_adapters_info;
 
 /*
@@ -327,12 +336,8 @@ GetProcessTimes_Proc get_process_times_fn = NULL;
 
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
-const char * const GetFileSecurity_Name =  "GetFileSecurityW";
-const char * const SetFileSecurity_Name =  "SetFileSecurityW";
 #else
 const char * const LookupAccountSid_Name = "LookupAccountSidA";
-const char * const GetFileSecurity_Name =  "GetFileSecurityA";
-const char * const SetFileSecurity_Name =  "SetFileSecurityA";
 #endif
 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     LPCTSTR lpSystemName,
@@ -356,16 +361,42 @@ typedef DWORD (WINAPI * GetSecurityInfo_Proc) (
     PACL *ppDacl,
     PACL *ppSacl,
     PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
-typedef BOOL (WINAPI * GetFileSecurity_Proc) (
-    LPCTSTR lpFileName,
+typedef BOOL (WINAPI * GetFileSecurityW_Proc) (
+    LPCWSTR lpFileName,
+    SECURITY_INFORMATION RequestedInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    DWORD nLength,
+    LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI * GetFileSecurityA_Proc) (
+    LPCSTR lpFileName,
     SECURITY_INFORMATION RequestedInformation,
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     DWORD nLength,
     LPDWORD lpnLengthNeeded);
-typedef BOOL (WINAPI *SetFileSecurity_Proc) (
-    LPCTSTR lpFileName,
+typedef BOOL (WINAPI *SetFileSecurityW_Proc) (
+    LPCWSTR lpFileName,
+    SECURITY_INFORMATION SecurityInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef BOOL (WINAPI *SetFileSecurityA_Proc) (
+    LPCSTR lpFileName,
     SECURITY_INFORMATION SecurityInformation,
     PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef DWORD (WINAPI *SetNamedSecurityInfoW_Proc) (
+    LPCWSTR lpObjectName,
+    SE_OBJECT_TYPE ObjectType,
+    SECURITY_INFORMATION SecurityInformation,
+    PSID psidOwner,
+    PSID psidGroup,
+    PACL pDacl,
+    PACL pSacl);
+typedef DWORD (WINAPI *SetNamedSecurityInfoA_Proc) (
+    LPCSTR lpObjectName,
+    SE_OBJECT_TYPE ObjectType,
+    SECURITY_INFORMATION SecurityInformation,
+    PSID psidOwner,
+    PSID psidGroup,
+    PACL pDacl,
+    PACL pSacl);
 typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
     PSECURITY_DESCRIPTOR pSecurityDescriptor,
     PSID *pOwner,
@@ -425,9 +456,13 @@ typedef BOOL (WINAPI * GetSystemTimes_Proc) (
     LPFILETIME lpIdleTime,
     LPFILETIME lpKernelTime,
     LPFILETIME lpUserTime);
-typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
-    LPTSTR lpSymlinkFileName,
-    LPTSTR lpTargetFileName,
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkW_Proc) (
+    LPCWSTR lpSymlinkFileName,
+    LPCWSTR lpTargetFileName,
+    DWORD  dwFlags);
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkA_Proc) (
+    LPCSTR lpSymlinkFileName,
+    LPCSTR lpTargetFileName,
     DWORD  dwFlags);
 typedef BOOL (WINAPI *ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
     LPCTSTR StringSecurityDescriptor,
@@ -445,6 +480,9 @@ typedef DWORD (WINAPI *GetAdaptersInfo_Proc) (
     PIP_ADAPTER_INFO pAdapterInfo,
     PULONG pOutBufLen);
 
+int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int);
+int (WINAPI *pWideCharToMultiByte)(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL);
+
   /* ** A utility function ** */
 static BOOL
 is_windows_9x (void)
@@ -679,64 +717,184 @@ get_security_info (HANDLE handle,
 }
 
 static BOOL WINAPI
-get_file_security (LPCTSTR lpFileName,
+get_file_security (const char *lpFileName,
                   SECURITY_INFORMATION RequestedInformation,
                   PSECURITY_DESCRIPTOR pSecurityDescriptor,
                   DWORD nLength,
                   LPDWORD lpnLengthNeeded)
 {
-  static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
+  static GetFileSecurityA_Proc s_pfn_Get_File_SecurityA = NULL;
+  static GetFileSecurityW_Proc s_pfn_Get_File_SecurityW = NULL;
   HMODULE hm_advapi32 = NULL;
   if (is_windows_9x () == TRUE)
     {
       errno = ENOTSUP;
       return FALSE;
     }
-  if (g_b_init_get_file_security == 0)
+  if (w32_unicode_filenames)
     {
-      g_b_init_get_file_security = 1;
-      hm_advapi32 = LoadLibrary ("Advapi32.dll");
-      s_pfn_Get_File_Security =
-        (GetFileSecurity_Proc) GetProcAddress (
-            hm_advapi32, GetFileSecurity_Name);
+      wchar_t filename_w[MAX_PATH];
+
+      if (g_b_init_get_file_security_w == 0)
+       {
+         g_b_init_get_file_security_w = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Get_File_SecurityW =
+           (GetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+                                                  "GetFileSecurityW");
+       }
+      if (s_pfn_Get_File_SecurityW == NULL)
+       {
+         errno = ENOTSUP;
+         return FALSE;
+       }
+      filename_to_utf16 (lpFileName, filename_w);
+      return (s_pfn_Get_File_SecurityW (filename_w, RequestedInformation,
+                                       pSecurityDescriptor, nLength,
+                                       lpnLengthNeeded));
     }
-  if (s_pfn_Get_File_Security == NULL)
+  else
     {
-      errno = ENOTSUP;
-      return FALSE;
+      char filename_a[MAX_PATH];
+
+      if (g_b_init_get_file_security_a == 0)
+       {
+         g_b_init_get_file_security_a = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Get_File_SecurityA =
+           (GetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+                                                  "GetFileSecurityA");
+       }
+      if (s_pfn_Get_File_SecurityA == NULL)
+       {
+         errno = ENOTSUP;
+         return FALSE;
+       }
+      filename_to_ansi (lpFileName, filename_a);
+      return (s_pfn_Get_File_SecurityA (filename_a, RequestedInformation,
+                                       pSecurityDescriptor, nLength,
+                                       lpnLengthNeeded));
     }
-  return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
-                                  pSecurityDescriptor, nLength,
-                                  lpnLengthNeeded));
 }
 
 static BOOL WINAPI
-set_file_security (LPCTSTR lpFileName,
+set_file_security (const char *lpFileName,
                   SECURITY_INFORMATION SecurityInformation,
                   PSECURITY_DESCRIPTOR pSecurityDescriptor)
 {
-  static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
+  static SetFileSecurityW_Proc s_pfn_Set_File_SecurityW = NULL;
+  static SetFileSecurityA_Proc s_pfn_Set_File_SecurityA = NULL;
   HMODULE hm_advapi32 = NULL;
   if (is_windows_9x () == TRUE)
     {
       errno = ENOTSUP;
       return FALSE;
     }
-  if (g_b_init_set_file_security == 0)
+  if (w32_unicode_filenames)
     {
-      g_b_init_set_file_security = 1;
-      hm_advapi32 = LoadLibrary ("Advapi32.dll");
-      s_pfn_Set_File_Security =
-        (SetFileSecurity_Proc) GetProcAddress (
-            hm_advapi32, SetFileSecurity_Name);
+      wchar_t filename_w[MAX_PATH];
+
+      if (g_b_init_set_file_security_w == 0)
+       {
+         g_b_init_set_file_security_w = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_File_SecurityW =
+           (SetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+                                                   "SetFileSecurityW");
+       }
+      if (s_pfn_Set_File_SecurityW == NULL)
+       {
+         errno = ENOTSUP;
+         return FALSE;
+       }
+      filename_to_utf16 (lpFileName, filename_w);
+      return (s_pfn_Set_File_SecurityW (filename_w, SecurityInformation,
+                                       pSecurityDescriptor));
     }
-  if (s_pfn_Set_File_Security == NULL)
+  else
+    {
+      char filename_a[MAX_PATH];
+
+      if (g_b_init_set_file_security_a == 0)
+       {
+         g_b_init_set_file_security_a = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_File_SecurityA =
+           (SetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+                                                   "SetFileSecurityA");
+       }
+      if (s_pfn_Set_File_SecurityA == NULL)
+       {
+         errno = ENOTSUP;
+         return FALSE;
+       }
+      filename_to_ansi (lpFileName, filename_a);
+      return (s_pfn_Set_File_SecurityA (filename_a, SecurityInformation,
+                                       pSecurityDescriptor));
+    }
+}
+
+static DWORD WINAPI
+set_named_security_info (LPCTSTR lpObjectName,
+                        SE_OBJECT_TYPE ObjectType,
+                        SECURITY_INFORMATION SecurityInformation,
+                        PSID psidOwner,
+                        PSID psidGroup,
+                        PACL pDacl,
+                        PACL pSacl)
+{
+  static SetNamedSecurityInfoW_Proc s_pfn_Set_Named_Security_InfoW = NULL;
+  static SetNamedSecurityInfoA_Proc s_pfn_Set_Named_Security_InfoA = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
     {
       errno = ENOTSUP;
-      return FALSE;
+      return ENOTSUP;
+    }
+  if (w32_unicode_filenames)
+    {
+      wchar_t filename_w[MAX_PATH];
+
+      if (g_b_init_set_named_security_info_w == 0)
+       {
+         g_b_init_set_named_security_info_w = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_Named_Security_InfoW =
+           (SetNamedSecurityInfoW_Proc) GetProcAddress (hm_advapi32,
+                                                        "SetNamedSecurityInfoW");
+       }
+      if (s_pfn_Set_Named_Security_InfoW == NULL)
+       {
+         errno = ENOTSUP;
+         return ENOTSUP;
+       }
+      filename_to_utf16 (lpObjectName, filename_w);
+      return (s_pfn_Set_Named_Security_InfoW (filename_w, ObjectType,
+                                             SecurityInformation, psidOwner,
+                                             psidGroup, pDacl, pSacl));
+    }
+  else
+    {
+      char filename_a[MAX_PATH];
+
+      if (g_b_init_set_named_security_info_a == 0)
+       {
+         g_b_init_set_named_security_info_a = 1;
+         hm_advapi32 = LoadLibrary ("Advapi32.dll");
+         s_pfn_Set_Named_Security_InfoA =
+           (SetNamedSecurityInfoA_Proc) GetProcAddress (hm_advapi32, 
+                                                        "SetNamedSecurityInfoA");
+       }
+      if (s_pfn_Set_Named_Security_InfoA == NULL)
+       {
+         errno = ENOTSUP;
+         return ENOTSUP;
+       }
+      filename_to_ansi (lpObjectName, filename_a);
+      return (s_pfn_Set_Named_Security_InfoA (filename_a, ObjectType,
+                                             SecurityInformation, psidOwner,
+                                             psidGroup, pDacl, pSacl));
     }
-  return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
-                                  pSecurityDescriptor));
 }
 
 static BOOL WINAPI
@@ -973,11 +1131,12 @@ get_system_times (LPFILETIME lpIdleTime,
 }
 
 static BOOLEAN WINAPI
-create_symbolic_link (LPTSTR lpSymlinkFilename,
-                     LPTSTR lpTargetFileName,
+create_symbolic_link (LPCSTR lpSymlinkFilename,
+                     LPCSTR lpTargetFileName,
                      DWORD dwFlags)
 {
-  static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
+  static CreateSymbolicLinkW_Proc s_pfn_Create_Symbolic_LinkW = NULL;
+  static CreateSymbolicLinkA_Proc s_pfn_Create_Symbolic_LinkA = NULL;
   BOOLEAN retval;
 
   if (is_windows_9x () == TRUE)
@@ -985,39 +1144,74 @@ create_symbolic_link (LPTSTR lpSymlinkFilename,
       errno = ENOSYS;
       return 0;
     }
-  if (g_b_init_create_symbolic_link == 0)
+  if (w32_unicode_filenames)
     {
-      g_b_init_create_symbolic_link = 1;
-#ifdef _UNICODE
-      s_pfn_Create_Symbolic_Link =
-       (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
-                                                "CreateSymbolicLinkW");
-#else
-      s_pfn_Create_Symbolic_Link =
-       (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
-                                                "CreateSymbolicLinkA");
-#endif
+      wchar_t symfn_w[MAX_PATH], tgtfn_w[MAX_PATH];
+
+      if (g_b_init_create_symbolic_link_w == 0)
+       {
+         g_b_init_create_symbolic_link_w = 1;
+         s_pfn_Create_Symbolic_LinkW =
+           (CreateSymbolicLinkW_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                    "CreateSymbolicLinkW");
+       }
+      if (s_pfn_Create_Symbolic_LinkW == NULL)
+       {
+         errno = ENOSYS;
+         return 0;
+       }
+
+      filename_to_utf16 (lpSymlinkFilename, symfn_w);
+      filename_to_utf16 (lpTargetFileName, tgtfn_w);
+      retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+      /* If we were denied creation of the symlink, try again after
+        enabling the SeCreateSymbolicLinkPrivilege for our process.  */
+      if (!retval)
+       {
+         TOKEN_PRIVILEGES priv_current;
+
+         if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+                               &priv_current))
+           {
+             retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+             restore_privilege (&priv_current);
+             revert_to_self ();
+           }
+       }
     }
-  if (s_pfn_Create_Symbolic_Link == NULL)
+  else
     {
-      errno = ENOSYS;
-      return 0;
-    }
+      char symfn_a[MAX_PATH], tgtfn_a[MAX_PATH];
 
-  retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
-                                      dwFlags);
-  /* If we were denied creation of the symlink, try again after
-     enabling the SeCreateSymbolicLinkPrivilege for our process.  */
-  if (!retval)
-    {
-      TOKEN_PRIVILEGES priv_current;
+      if (g_b_init_create_symbolic_link_a == 0)
+       {
+         g_b_init_create_symbolic_link_a = 1;
+         s_pfn_Create_Symbolic_LinkA =
+           (CreateSymbolicLinkA_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                    "CreateSymbolicLinkA");
+       }
+      if (s_pfn_Create_Symbolic_LinkA == NULL)
+       {
+         errno = ENOSYS;
+         return 0;
+       }
 
-      if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
+      filename_to_ansi (lpSymlinkFilename, symfn_a);
+      filename_to_ansi (lpTargetFileName, tgtfn_a);
+      retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+      /* If we were denied creation of the symlink, try again after
+        enabling the SeCreateSymbolicLinkPrivilege for our process.  */
+      if (!retval)
        {
-         retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
-                                              dwFlags);
-         restore_privilege (&priv_current);
-         revert_to_self ();
+         TOKEN_PRIVILEGES priv_current;
+
+         if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+                               &priv_current))
+           {
+             retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+             restore_privilege (&priv_current);
+             revert_to_self ();
+           }
        }
     }
   return retval;
@@ -1184,6 +1378,110 @@ w32_valid_pointer_p (void *p, int size)
 
 \f
 
+/* Here's an overview of how the Windows build supports file names
+   that cannot be encoded by the current system codepage.
+
+   From the POV of Lisp and layers of C code above the functions here,
+   Emacs on Windows pretends that its file names are encoded in UTF-8;
+   see encode_file and decode_file on coding.c.  Any file name that is
+   passed as a unibyte string to C functions defined here is assumed
+   to be in UTF-8 encoding.  Any file name returned by functions
+   defined here must be in UTF-8 encoding, with only a few exceptions
+   reserved for a couple of special cases.  (Be sure to use
+   MAX_UTF8_PATH for char arrays that store UTF-8 encoded file names,
+   as they can be much longer than MAX_PATH!)
+
+   The UTF-8 encoded file names cannot be passed to system APIs, as
+   Windows does not support that.  Therefore, they are converted
+   either to UTF-16 or to the ANSI codepage, depending on the value of
+   w32-unicode-filenames, before calling any system APIs or CRT library
+   functions.  The default value of that variable is determined by the
+   OS on which Emacs runs: nil on Windows 9X and t otherwise, but the
+   user can change that default (although I don't see why would she
+   want to).
+
+   The 4 functions defined below, filename_to_utf16, filename_to_ansi,
+   filename_from_utf16, and filename_from_ansi, are the workhorses of
+   these conversions.  They rely on Windows native APIs
+   MultiByteToWideChar and WideCharToMultiByte; we cannot use
+   functions from coding.c here, because they allocate memory, which
+   is a bad idea on the level of libc, which is what the functions
+   here emulate.  (If you worry about performance due to constant
+   conversion back and forth from UTF-8 to UTF-16, then don't: first,
+   it was measured to take only a few microseconds on a not-so-fast
+   machine, and second, that's exactly what the ANSI APIs we used
+   before did anyway, because they are just thin wrappers around the
+   Unicode APIs.)
+
+   The variables file-name-coding-system and default-file-name-coding-system
+   still exist, but are actually used only when a file name needs to
+   be converted to the ANSI codepage.  This happens all the time when
+   w32-unicode-filenames is nil, but can also happen from time to time
+   when it is t.  Otherwise, these variables have no effect on file-name
+   encoding when w32-unicode-filenames is t; this is similar to
+   selection-coding-system.
+
+   This arrangement works very well, but it has a few gotchas and
+   limitations:
+
+   . Lisp code that encodes or decodes file names manually should
+     normally use 'utf-8' as the coding-system on Windows,
+     disregarding file-name-coding-system.  This is a somewhat
+     unpleasant consequence, but it cannot be avoided.  Fortunately,
+     very few Lisp packages need to do that.
+
+     More generally, passing to library functions (e.g., fopen or
+     opendir) file names already encoded in the ANSI codepage is
+     explicitly *verboten*, as all those functions, as shadowed and
+     emulated here, assume they will receive UTF-8 encoded file names.
+
+     For the same reasons, no CRT function or Win32 API can be called
+     directly in Emacs sources, without either converting the file
+     names from UTF-8 to UTF-16 or ANSI codepage, or going through
+     some shadowing function defined here.
+
+   . Environment variables stored in Vprocess_environment are encoded
+     in the ANSI codepage, so if getenv/egetenv is used for a variable
+     whose value is a file name or a list of directories, it needs to
+     be converted to UTF-8, before it is used as argument to functions
+     or decoded into a Lisp string.
+
+   . File names passed to external libraries, like the image libraries
+     and GnuTLS, need special handling.  These libraries generally
+     don't support UTF-16 or UTF-8 file names, so they must get file
+     names encoded in the ANSI codepage.  To facilitate using these
+     libraries with file names that are not encodable in the ANSI
+     codepage, use the function ansi_encode_filename, which will try
+     to use the short 8+3 alias of a file name if that file name is
+     not encodable in the ANSI codepage.  See image.c and gnutls.c for
+     examples of how this should be done.
+
+   . Running subprocesses in non-ASCII directories and with non-ASCII
+     file arguments is limited to the current codepage (even though
+     Emacs is perfectly capable of finding an executable program file
+     in a directory whose name cannot be encoded in the current
+     codepage).  This is because the command-line arguments are
+     encoded _before_ they get to the w32-specific level, and the
+     encoding is not known in advance (it doesn't have to be the
+     current ANSI codepage), so w32proc.c functions cannot re-encode
+     them in UTF-16.  This should be fixed, but will also require
+     changes in cmdproxy.  The current limitation is not terribly bad
+     anyway, since very few, if any, Windows console programs that are
+     likely to be invoked by Emacs support UTF-16 encoded command
+     lines.
+
+   . For similar reasons, server.el and emacsclient are also limited
+     to the current ANSI codepage for now.
+
+   . Emacs itself can only handle command-line arguments encoded in
+     the current codepage.
+
+   . Turning on w32-unicode-filename on Windows 9X (if it at all
+     works) requires UNICOWS.DLL, which is thus a requirement even in
+     non-GUI sessions, something the we previously avoided.  */
+
+\f
+
 /* Converting file names from UTF-8 to either UTF-16 or the ANSI
    codepage defined by file-name-coding-system.  */
 
@@ -1247,11 +1545,11 @@ codepage_for_filenames (CPINFO *cp_info)
   return file_name_codepage;
 }
 
-static int
+int
 filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
 {
-  int result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
-                                   fn_out, MAX_PATH);
+  int result = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
+                                    fn_out, MAX_PATH);
 
   if (!result)
     {
@@ -1274,11 +1572,11 @@ filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
   return 0;
 }
 
-static int
+int
 filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
 {
-  int result = WideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
-                                   fn_out, MAX_UTF8_PATH, NULL, NULL);
+  int result = pWideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
+                                    fn_out, MAX_UTF8_PATH, NULL, NULL);
 
   if (!result)
     {
@@ -1301,7 +1599,7 @@ filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
   return 0;
 }
 
-static int
+int
 filename_to_ansi (const char *fn_in, char *fn_out)
 {
   wchar_t fn_utf16[MAX_PATH];
@@ -1311,8 +1609,8 @@ filename_to_ansi (const char *fn_in, char *fn_out)
       int result;
       int codepage = codepage_for_filenames (NULL);
 
-      result  = WideCharToMultiByte (codepage, 0, fn_utf16, -1,
-                                    fn_out, MAX_PATH, NULL, NULL);
+      result  = pWideCharToMultiByte (codepage, 0, fn_utf16, -1,
+                                     fn_out, MAX_PATH, NULL, NULL);
       if (!result)
        {
          DWORD err = GetLastError ();
@@ -1341,8 +1639,8 @@ filename_from_ansi (const char *fn_in, char *fn_out)
 {
   wchar_t fn_utf16[MAX_PATH];
   int codepage = codepage_for_filenames (NULL);
-  int result = MultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
-                                   fn_utf16, MAX_PATH);
+  int result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1,
+                                    fn_utf16, MAX_PATH);
 
   if (!result)
     {
@@ -1411,7 +1709,7 @@ static unsigned num_of_processors;
 /* We maintain 1-sec samples for the last 16 minutes in a circular buffer.  */
 static struct load_sample samples[16*60];
 static int first_idx = -1, last_idx = -1;
-static int max_idx = sizeof (samples) / sizeof (samples[0]);
+static int max_idx = ARRAYELTS (samples);
 
 static int
 buf_next (int from)
@@ -1521,9 +1819,28 @@ getloadavg (double loadavg[], int nelem)
   ULONGLONG idle, kernel, user;
   time_t now = time (NULL);
 
+  /* If system time jumped back for some reason, delete all samples
+     whose time is later than the current wall-clock time.  This
+     prevents load average figures from becoming frozen for prolonged
+     periods of time, when system time is reset backwards.  */
+  if (last_idx >= 0)
+    {
+      while (difftime (now, samples[last_idx].sample_time) < -1.0)
+       {
+         if (last_idx == first_idx)
+           {
+             first_idx = last_idx = -1;
+             break;
+           }
+         last_idx = buf_prev (last_idx);
+       }
+    }
+
   /* Store another sample.  We ignore samples that are less than 1 sec
      apart.  */
-  if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
+  if (last_idx < 0
+      || (difftime (now, samples[last_idx].sample_time)
+         >= 1.0 - 2*DBL_EPSILON*now))
     {
       sample_system_load (&idle, &kernel, &user);
       last_idx = buf_next (last_idx);
@@ -1822,7 +2139,7 @@ normalize_filename (register char *fp, char path_sep)
 
   while (*fp)
     {
-      if (*fp == '/' || *fp == '\\')
+      if ((*fp == '/' || *fp == '\\') && *fp != path_sep)
        *fp = path_sep;
       fp++;
     }
@@ -1913,10 +2230,10 @@ parse_root (const char * name, const char ** pPath)
 static int
 get_long_basename (char * name, char * buf, int size)
 {
-  HANDLE dir_handle;
+  HANDLE dir_handle = INVALID_HANDLE_VALUE;
   char fname_utf8[MAX_UTF8_PATH];
   int len = 0;
-  int cstatus;
+  int cstatus = -1;
 
   /* Must be valid filename, no wild cards or other invalid characters.  */
   if (strpbrk (name, "*?|<>\""))
@@ -1938,9 +2255,28 @@ get_long_basename (char * name, char * buf, int size)
       WIN32_FIND_DATAA find_data_ansi;
 
       filename_to_ansi (name, fname_ansi);
-      dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
-      if (dir_handle != INVALID_HANDLE_VALUE)
-       cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+      /* If the ANSI name includes ? characters, it is not encodable
+        in the ANSI codepage.  In that case, we deliver the question
+        marks to the caller; calling FindFirstFileA in this case
+        could return some unrelated file name in the same
+        directory.  */
+      if (_mbspbrk (fname_ansi, "?"))
+       {
+         /* Find the basename of fname_ansi.  */
+         char *p = strrchr (fname_ansi, '\\');
+
+         if (!p)
+           p = fname_ansi;
+         else
+           p++;
+         cstatus = filename_from_ansi (p, fname_utf8);
+       }
+      else
+       {
+         dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
+         if (dir_handle != INVALID_HANDLE_VALUE)
+           cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+       }
     }
 
   if (cstatus == 0 && (len = strlen (fname_utf8)) < size)
@@ -2029,6 +2365,36 @@ w32_get_short_filename (char * name, char * buf, int size)
     }
 }
 
+/* Re-encode FILENAME, a UTF-8 encoded unibyte string, using the
+   MS-Windows ANSI codepage.  If FILENAME includes characters not
+   supported by the ANSI codepage, return the 8+3 alias of FILENAME,
+   if it exists.  This is needed because the w32 build wants to
+   support file names outside of the system locale, but image
+   libraries typically don't support wide (a.k.a. "Unicode") APIs
+   required for that.  */
+
+Lisp_Object
+ansi_encode_filename (Lisp_Object filename)
+{
+  Lisp_Object encoded_filename;
+  char fname[MAX_PATH];
+
+  filename_to_ansi (SSDATA (filename), fname);
+  if (_mbspbrk (fname, "?"))
+    {
+      char shortname[MAX_PATH];
+
+      if (w32_get_short_filename (SDATA (filename), shortname, MAX_PATH))
+       {
+         dostounix_filename (shortname);
+         encoded_filename = build_string (shortname);
+       }
+    }
+  else
+    encoded_filename = build_unibyte_string (fname);
+  return encoded_filename;
+}
+
 static int
 is_unc_volume (const char *filename)
 {
@@ -2049,7 +2415,6 @@ unsetenv (const char *name)
 {
   char *var;
   size_t name_len;
-  int retval;
 
   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
     {
@@ -2137,6 +2502,8 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
   return (NULL);
 }
 
+/* The argv[] array holds ANSI-encoded strings, and so this function
+   works with ANS_encoded strings.  */
 void
 init_environment (char ** argv)
 {
@@ -2146,7 +2513,7 @@ init_environment (char ** argv)
 
   int i;
 
-  const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
+  const int imax = ARRAYELTS (tempdirs);
 
   /* Implementation note: This function explicitly works with ANSI
      file names, not with UTF-8 encoded file names.  This is because
@@ -2219,7 +2586,7 @@ init_environment (char ** argv)
       {"LANG", NULL},
     };
 
-#define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
+#define N_ENV_VARS ARRAYELTS (dflt_envvars)
 
     /* We need to copy dflt_envvars[] and work on the copy because we
        don't want the dumped Emacs to inherit the values of
@@ -2280,7 +2647,7 @@ init_environment (char ** argv)
       char *p;
       char modname[MAX_PATH];
 
-      if (!GetModuleFileName (NULL, modname, MAX_PATH))
+      if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
        emacs_abort ();
       if ((p = _mbsrchr (modname, '\\')) == NULL)
        emacs_abort ();
@@ -2444,7 +2811,7 @@ init_environment (char ** argv)
   {
     static char modname[MAX_PATH];
 
-    if (!GetModuleFileName (NULL, modname, MAX_PATH))
+    if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
       emacs_abort ();
     argv[0] = modname;
   }
@@ -2946,6 +3313,9 @@ static int    dir_is_fat;
 static char   dir_pathname[MAX_UTF8_PATH];
 static WIN32_FIND_DATAW dir_find_data_w;
 static WIN32_FIND_DATAA dir_find_data_a;
+#define DIR_FIND_DATA_W 1
+#define DIR_FIND_DATA_A 2
+static int    last_dir_find_data = -1;
 
 /* Support shares on a network resource as subdirectories of a read-only
    root directory. */
@@ -2977,7 +3347,7 @@ sys_opendir (const char *filename)
        return NULL;
     }
 
-  if (!(dirp = (DIR *) malloc (sizeof (DIR))))
+  if (!(dirp = xmalloc (sizeof (DIR))))
     return NULL;
 
   dirp->dd_fd = 0;
@@ -3051,7 +3421,13 @@ sys_readdir (DIR *dirp)
          char fna[MAX_PATH];
 
          filename_to_ansi (filename, fna);
-         dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
+         /* If FILENAME is not representable by the current ANSI
+            codepage, we don't want FindFirstFileA to interpret the
+            '?' characters as a wildcard.  */
+         if (_mbspbrk (fna, "?"))
+           dir_find_handle = INVALID_HANDLE_VALUE;
+         else
+           dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
        }
 
       if (dir_find_handle == INVALID_HANDLE_VALUE)
@@ -3084,6 +3460,7 @@ sys_readdir (DIR *dirp)
        }
       else
        filename_from_utf16 (dir_find_data_w.cFileName, dir_static.d_name);
+      last_dir_find_data = DIR_FIND_DATA_W;
     }
   else
     {
@@ -3110,6 +3487,7 @@ sys_readdir (DIR *dirp)
          _mbslwr (tem);
          filename_from_ansi (tem, dir_static.d_name);
        }
+      last_dir_find_data = DIR_FIND_DATA_A;
     }
 
   dir_static.d_namlen = strlen (dir_static.d_name);
@@ -3615,7 +3993,9 @@ sys_link (const char * old, const char * new)
 {
   HANDLE fileh;
   int   result = -1;
-  char oldname[MAX_PATH], newname[MAX_PATH];
+  char oldname[MAX_UTF8_PATH], newname[MAX_UTF8_PATH];
+  wchar_t oldname_w[MAX_PATH];
+  char oldname_a[MAX_PATH];
 
   if (old == NULL || new == NULL)
     {
@@ -3626,8 +4006,18 @@ sys_link (const char * old, const char * new)
   strcpy (oldname, map_w32_filename (old, NULL));
   strcpy (newname, map_w32_filename (new, NULL));
 
-  fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
-                     FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  if (w32_unicode_filenames)
+    {
+      filename_to_utf16 (oldname, oldname_w);
+      fileh = CreateFileW (oldname_w, 0, 0, NULL, OPEN_EXISTING,
+                          FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
+  else
+    {
+      filename_to_ansi (oldname, oldname_a);
+      fileh = CreateFileA (oldname_a, 0, 0, NULL, OPEN_EXISTING,
+                          FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
   if (fileh != INVALID_HANDLE_VALUE)
     {
       int wlen;
@@ -3644,8 +4034,11 @@ sys_link (const char * old, const char * new)
        WCHAR wbuffer[MAX_PATH];        /* extra space for link name */
       } data;
 
-      wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
-                                 data.wid.cStreamName, MAX_PATH);
+      /* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN
+        indicates that flag is unsupported for CP_UTF8, and OTOH says
+        it is the default anyway.  */
+      wlen = pMultiByteToWideChar (CP_UTF8, 0, newname, -1,
+                                  data.wid.cStreamName, MAX_PATH);
       if (wlen > 0)
        {
          LPVOID context = NULL;
@@ -3668,9 +4061,40 @@ sys_link (const char * old, const char * new)
            }
          else
            {
-             /* Should try mapping GetLastError to errno; for now just
-                indicate a general error (eg. links not supported).  */
-             errno = EINVAL;  // perhaps EMLINK?
+             DWORD err = GetLastError ();
+             DWORD attributes;
+
+             switch (err)
+               {
+               case ERROR_ACCESS_DENIED:
+                 /* This is what happens when OLDNAME is a directory,
+                    since Windows doesn't support hard links to
+                    directories.  Posix says to set errno to EPERM in
+                    that case.  */
+                 if (w32_unicode_filenames)
+                   attributes = GetFileAttributesW (oldname_w);
+                 else
+                   attributes = GetFileAttributesA (oldname_a);
+                 if (attributes != -1
+                     && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+                   errno = EPERM;
+                 else if (attributes == -1
+                          && is_unc_volume (oldname)
+                          && unc_volume_file_attributes (oldname) != -1)
+                   errno = EPERM;
+                 else
+                   errno = EACCES;
+                 break;
+               case ERROR_TOO_MANY_LINKS:
+                 errno = EMLINK;
+                 break;
+               case ERROR_NOT_SAME_DEVICE:
+                 errno = EXDEV;
+                 break;
+               default:
+                 errno = EINVAL;
+                 break;
+               }
            }
        }
 
@@ -3685,7 +4109,22 @@ sys_link (const char * old, const char * new)
 int
 sys_mkdir (const char * path)
 {
-  return _mkdir (map_w32_filename (path, NULL));
+  path = map_w32_filename (path, NULL);
+
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      return _wmkdir (path_w);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      return _mkdir (path_a);
+    }
 }
 
 int
@@ -3694,13 +4133,29 @@ sys_open (const char * path, int oflag, int mode)
   const char* mpath = map_w32_filename (path, NULL);
   int res = -1;
 
-  /* If possible, try to open file without _O_CREAT, to be able to
-     write to existing hidden and system files.  Force all file
-     handles to be non-inheritable. */
-  if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
-    res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
-  if (res < 0)
-    res = _open (mpath, oflag | _O_NOINHERIT, mode);
+  if (w32_unicode_filenames)
+    {
+      wchar_t mpath_w[MAX_PATH];
+
+      filename_to_utf16 (mpath, mpath_w);
+      /* If possible, try to open file without _O_CREAT, to be able to
+        write to existing hidden and system files.  Force all file
+        handles to be non-inheritable. */
+      if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+       res = _wopen (mpath_w, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+      if (res < 0)
+       res = _wopen (mpath_w, oflag | _O_NOINHERIT, mode);
+    }
+  else
+    {
+      char mpath_a[MAX_PATH];
+
+      filename_to_ansi (mpath, mpath_a);
+      if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+       res = _open (mpath_a, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+      if (res < 0)
+       res = _open (mpath_a, oflag | _O_NOINHERIT, mode);
+    }
 
   return res;
 }
@@ -3770,9 +4225,10 @@ int
 sys_rename_replace (const char *oldname, const char *newname, BOOL force)
 {
   BOOL result;
-  char temp[MAX_PATH];
+  char temp[MAX_UTF8_PATH], temp_a[MAX_PATH];;
   int newname_dev;
   int oldname_dev;
+  bool have_temp_a = false;
 
   /* MoveFile on Windows 95 doesn't correctly change the short file name
      alias in a number of circumstances (it is not easy to predict when
@@ -3797,17 +4253,20 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force)
       char * o;
       char * p;
       int    i = 0;
+      char oldname_a[MAX_PATH];
 
       oldname = map_w32_filename (oldname, NULL);
-      if ((o = strrchr (oldname, '\\')))
+      filename_to_ansi (oldname, oldname_a);
+      filename_to_ansi (temp, temp_a);
+      if ((o = strrchr (oldname_a, '\\')))
        o++;
       else
-       o = (char *) oldname;
+       o = (char *) oldname_a;
 
-      if ((p = strrchr (temp, '\\')))
+      if ((p = strrchr (temp_a, '\\')))
        p++;
       else
-       p = temp;
+       p = temp_a;
 
       do
        {
@@ -3815,12 +4274,13 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force)
             seems to make the second rename work properly.  */
          sprintf (p, "_.%s.%u", o, i);
          i++;
-         result = rename (oldname, temp);
+         result = rename (oldname_a, temp_a);
        }
       /* This loop must surely terminate!  */
       while (result < 0 && errno == EEXIST);
       if (result < 0)
        return -1;
+      have_temp_a = true;
     }
 
   /* If FORCE, emulate Unix behavior - newname is deleted if it already exists
@@ -3839,41 +4299,81 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force)
   /* volume_info is set indirectly by map_w32_filename.  */
   newname_dev = volume_info.serialnum;
 
-  result = rename (temp, newname);
-
-  if (result < 0 && force)
+  if (w32_unicode_filenames)
     {
-      DWORD w32err = GetLastError ();
+      wchar_t temp_w[MAX_PATH], newname_w[MAX_PATH];
 
-      if (errno == EACCES
-         && newname_dev != oldname_dev)
-       {
-         /* The implementation of `rename' on Windows does not return
-            errno = EXDEV when you are moving a directory to a
-            different storage device (ex. logical disk).  It returns
-            EACCES instead.  So here we handle such situations and
-            return EXDEV.  */
-         DWORD attributes;
-
-         if ((attributes = GetFileAttributes (temp)) != -1
-             && (attributes & FILE_ATTRIBUTE_DIRECTORY))
-           errno = EXDEV;
-       }
-      else if (errno == EEXIST)
+      filename_to_utf16 (temp, temp_w);
+      filename_to_utf16 (newname, newname_w);
+      result = _wrename (temp_w, newname_w);
+      if (result < 0 && force)
        {
-         if (_chmod (newname, 0666) != 0)
-           return result;
-         if (_unlink (newname) != 0)
-           return result;
-         result = rename (temp, newname);
+         DWORD w32err = GetLastError ();
+
+         if (errno == EACCES
+             && newname_dev != oldname_dev)
+           {
+             /* The implementation of `rename' on Windows does not return
+                errno = EXDEV when you are moving a directory to a
+                different storage device (ex. logical disk).  It returns
+                EACCES instead.  So here we handle such situations and
+                return EXDEV.  */
+             DWORD attributes;
+
+             if ((attributes = GetFileAttributesW (temp_w)) != -1
+                 && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+               errno = EXDEV;
+           }
+         else if (errno == EEXIST)
+           {
+             if (_wchmod (newname_w, 0666) != 0)
+               return result;
+             if (_wunlink (newname_w) != 0)
+               return result;
+             result = _wrename (temp_w, newname_w);
+           }
+         else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+                  && is_symlink (temp))
+           {
+             /* This is Windows prohibiting the user from creating a
+                symlink in another place, since that requires
+                privileges.  */
+             errno = EPERM;
+           }
        }
-      else if (w32err == ERROR_PRIVILEGE_NOT_HELD
-              && is_symlink (temp))
+    }
+  else
+    {
+      char newname_a[MAX_PATH];
+
+      if (!have_temp_a)
+       filename_to_ansi (temp, temp_a);
+      filename_to_ansi (newname, newname_a);
+      result = rename (temp_a, newname_a);
+      if (result < 0 && force)
        {
-         /* This is Windows prohibiting the user from creating a
-            symlink in another place, since that requires
-            privileges.  */
-         errno = EPERM;
+         DWORD w32err = GetLastError ();
+
+         if (errno == EACCES
+             && newname_dev != oldname_dev)
+           {
+             DWORD attributes;
+
+             if ((attributes = GetFileAttributesA (temp_a)) != -1
+                 && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+               errno = EXDEV;
+           }
+         else if (errno == EEXIST)
+           {
+             if (_chmod (newname_a, 0666) != 0)
+               return result;
+             if (_unlink (newname_a) != 0)
+               return result;
+             result = rename (temp_a, newname_a);
+           }
+         else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+                  && is_symlink (temp))
+           errno = EPERM;
        }
     }
 
@@ -3889,7 +4389,22 @@ sys_rename (char const *old, char const *new)
 int
 sys_rmdir (const char * path)
 {
-  return _rmdir (map_w32_filename (path, NULL));
+  path = map_w32_filename (path, NULL);
+
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      return _wrmdir (path_w);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      return _rmdir (path_a);
+    }
 }
 
 int
@@ -3897,9 +4412,23 @@ sys_unlink (const char * path)
 {
   path = map_w32_filename (path, NULL);
 
-  /* On Unix, unlink works without write permission. */
-  _chmod (path, 0666);
-  return _unlink (path);
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      /* On Unix, unlink works without write permission. */
+      _wchmod (path_w, 0666);
+      return _wunlink (path_w);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      _chmod (path_a, 0666);
+      return _unlink (path_a);
+    }
 }
 
 static FILETIME utc_base_ft;
@@ -4173,18 +4702,21 @@ is_slow_fs (const char *name)
 }
 
 /* If this is non-zero, the caller wants accurate information about
-   file's owner and group, which could be expensive to get.  */
+   file's owner and group, which could be expensive to get.  dired.c
+   uses this flag when needed for the job at hand.  */
 int w32_stat_get_owner_group;
 
 /* MSVC stat function can't cope with UNC names and has other bugs, so
    replace it with our own.  This also allows us to calculate consistent
-   inode values and owner/group without hacks in the main Emacs code. */
+   inode values and owner/group without hacks in the main Emacs code,
+   and support file names encoded in UTF-8. */
 
 static int
 stat_worker (const char * path, struct stat * buf, int follow_symlinks)
 {
   char *name, *save_name, *r;
-  WIN32_FIND_DATA wfd;
+  WIN32_FIND_DATAW wfd_w;
+  WIN32_FIND_DATAA wfd_a;
   HANDLE fh;
   unsigned __int64 fake_inode = 0;
   int permission;
@@ -4196,7 +4728,8 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
   DWORD access_rights = 0;
   DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
   FILETIME ctime, atime, wtime;
-  int dbcs_p;
+  wchar_t name_w[MAX_PATH];
+  char name_a[MAX_PATH];
 
   if (path == NULL || buf == NULL)
     {
@@ -4206,20 +4739,16 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
 
   save_name = name = (char *) map_w32_filename (path, &path);
   /* Must be valid filename, no wild cards or other invalid
-     characters.  We use _mbspbrk to support multibyte strings that
-     might look to strpbrk as if they included literal *, ?, and other
-     characters mentioned below that are disallowed by Windows
-     filesystems.  */
-  if (_mbspbrk (name, "*?|<>\""))
+     characters.  */
+  if (strpbrk (name, "*?|<>\""))
     {
       errno = ENOENT;
       return -1;
     }
 
-  /* Remove trailing directory separator, unless name is the root
-     directory of a drive or UNC volume in which case ensure there
-     is a trailing separator. */
   len = strlen (name);
+  /* Allocate 1 extra byte so that we could append a slash to a root
+     directory, down below.  */
   name = strcpy (alloca (len + 2), name);
 
   /* Avoid a somewhat costly call to is_symlink if the filesystem
@@ -4261,13 +4790,26 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
       if (is_windows_9x () != TRUE)
        access_rights |= READ_CONTROL;
 
-      fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING,
-                      file_flags, NULL);
-      /* If CreateFile fails with READ_CONTROL, try again with zero as
-        access rights.  */
-      if (fh == INVALID_HANDLE_VALUE && access_rights)
-       fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
-                        file_flags, NULL);
+      if (w32_unicode_filenames)
+       {
+         filename_to_utf16 (name, name_w);
+         fh = CreateFileW (name_w, access_rights, 0, NULL, OPEN_EXISTING,
+                          file_flags, NULL);
+         /* If CreateFile fails with READ_CONTROL, try again with
+            zero as access rights.  */
+         if (fh == INVALID_HANDLE_VALUE && access_rights)
+           fh = CreateFileW (name_w, 0, 0, NULL, OPEN_EXISTING,
+                            file_flags, NULL);
+       }
+      else
+       {
+         filename_to_ansi (name, name_a);
+         fh = CreateFileA (name_a, access_rights, 0, NULL, OPEN_EXISTING,
+                          file_flags, NULL);
+         if (fh == INVALID_HANDLE_VALUE && access_rights)
+           fh = CreateFileA (name_a, 0, 0, NULL, OPEN_EXISTING,
+                            file_flags, NULL);
+       }
       if (fh == INVALID_HANDLE_VALUE)
        goto no_true_file_attributes;
 
@@ -4394,7 +4936,6 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
         did not ask for extra precision, resolving symlinks will fly
         in the face of that request, since the user then wants the
         lightweight version of the code.  */
-      dbcs_p = max_filename_mbslen () > 1;
       rootdir = (path >= save_name + len - 1
                 && (IS_DIRECTORY_SEP (*path) || *path == 0));
 
@@ -4422,19 +4963,9 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
        }
       else if (rootdir)
        {
-         if (!dbcs_p)
-           {
-             if (!IS_DIRECTORY_SEP (name[len-1]))
-               strcat (name, "\\");
-           }
-         else
-           {
-             char *end = name + len;
-             char *n = CharPrevExA (file_name_codepage, name, end, 0);
-
-             if (!IS_DIRECTORY_SEP (*n))
-               strcat (name, "\\");
-           }
+         /* Make sure root directories end in a slash.  */
+         if (!IS_DIRECTORY_SEP (name[len-1]))
+           strcat (name, "\\");
          if (GetDriveType (name) < 2)
            {
              errno = ENOENT;
@@ -4446,51 +4977,67 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
        }
       else
        {
-         if (!dbcs_p)
-           {
-             if (IS_DIRECTORY_SEP (name[len-1]))
-               name[len - 1] = 0;
-           }
-         else
-           {
-             char *end = name + len;
-             char *n = CharPrevExA (file_name_codepage, name, end, 0);
+         int have_wfd = -1;
 
-             if (IS_DIRECTORY_SEP (*n))
-               *n = 0;
-           }
+         /* Make sure non-root directories do NOT end in a slash,
+            otherwise FindFirstFile might fail.  */
+         if (IS_DIRECTORY_SEP (name[len-1]))
+           name[len - 1] = 0;
 
          /* (This is hacky, but helps when doing file completions on
             network drives.)  Optimize by using information available from
             active readdir if possible.  */
          len = strlen (dir_pathname);
-         if (!dbcs_p)
-           {
-             if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
-               len--;
-           }
-         else
-           {
-             char *end = dir_pathname + len;
-             char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
-
-             if (IS_DIRECTORY_SEP (*n))
-               len--;
-           }
+         if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+           len--;
          if (dir_find_handle != INVALID_HANDLE_VALUE
+             && last_dir_find_data != -1
              && !(is_a_symlink && follow_symlinks)
-             && strnicmp (save_name, dir_pathname, len) == 0
+             /* The 2 file-name comparisons below support only ASCII
+                characters, and will lose (compare not equal) when
+                the file names include non-ASCII characters that are
+                the same but for the case.  However, doing this
+                properly involves: (a) converting both file names to
+                UTF-16, (b) lower-casing both names using CharLowerW,
+                and (c) comparing the results; this would be quite a
+                bit slower, whereas Plan B is for users who want
+                lightweight albeit inaccurate version of 'stat'.  */
+             && c_strncasecmp (save_name, dir_pathname, len) == 0
              && IS_DIRECTORY_SEP (name[len])
              && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
            {
+             have_wfd = last_dir_find_data;
              /* This was the last entry returned by readdir.  */
-             wfd = dir_find_data_a; /* FIXME!!! */
+             if (last_dir_find_data == DIR_FIND_DATA_W)
+               wfd_w = dir_find_data_w;
+             else
+               wfd_a = dir_find_data_a;
            }
          else
            {
              logon_network_drive (name);
 
-             fh = FindFirstFile (name, &wfd);
+             if (w32_unicode_filenames)
+               {
+                 filename_to_utf16 (name, name_w);
+                 fh = FindFirstFileW (name_w, &wfd_w);
+                 have_wfd = DIR_FIND_DATA_W;
+               }
+             else
+               {
+                 filename_to_ansi (name, name_a);
+                 /* If NAME includes characters not representable by
+                    the current ANSI codepage, filename_to_ansi
+                    usually replaces them with a '?'.  We don't want
+                    to let FindFirstFileA interpret those as wildcards,
+                    and "succeed", returning us data from some random
+                    file in the same directory.  */
+                 if (_mbspbrk (name_a, "?"))
+                   fh = INVALID_HANDLE_VALUE;
+                 else
+                   fh = FindFirstFileA (name_a, &wfd_a);
+                 have_wfd = DIR_FIND_DATA_A;
+               }
              if (fh == INVALID_HANDLE_VALUE)
                {
                  errno = ENOENT;
@@ -4500,12 +5047,24 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
            }
          /* Note: if NAME is a symlink, the information we get from
             FindFirstFile is for the symlink, not its target.  */
-         fattrs = wfd.dwFileAttributes;
-         ctime = wfd.ftCreationTime;
-         atime = wfd.ftLastAccessTime;
-         wtime = wfd.ftLastWriteTime;
-         fs_high = wfd.nFileSizeHigh;
-         fs_low = wfd.nFileSizeLow;
+         if (have_wfd == DIR_FIND_DATA_W)
+           {
+             fattrs = wfd_w.dwFileAttributes;
+             ctime = wfd_w.ftCreationTime;
+             atime = wfd_w.ftLastAccessTime;
+             wtime = wfd_w.ftLastWriteTime;
+             fs_high = wfd_w.nFileSizeHigh;
+             fs_low = wfd_w.nFileSizeLow;
+           }
+         else
+           {
+             fattrs = wfd_a.dwFileAttributes;
+             ctime = wfd_a.ftCreationTime;
+             atime = wfd_a.ftLastAccessTime;
+             wtime = wfd_a.ftLastWriteTime;
+             fs_high = wfd_a.nFileSizeHigh;
+             fs_low = wfd_a.nFileSizeLow;
+           }
          fake_inode = 0;
          nlinks = 1;
          serialnum = volume_info.serialnum;
@@ -4520,18 +5079,6 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
       get_file_owner_and_group (NULL, buf);
     }
 
-#if 0
-  /* Not sure if there is any point in this.  */
-  if (!NILP (Vw32_generate_fake_inodes))
-    fake_inode = generate_inode_val (name);
-  else if (fake_inode == 0)
-    {
-      /* For want of something better, try to make everything unique.  */
-      static DWORD gen_num = 0;
-      fake_inode = ++gen_num;
-    }
-#endif
-
   buf->st_ino = fake_inode;
 
   buf->st_dev = serialnum;
@@ -4590,11 +5137,14 @@ fstatat (int fd, char const *name, struct stat *st, int flags)
 
      FIXME: Add proper support for fdopendir, fstatat, readlinkat.
      Gnulib does this and can serve as a model.  */
-  char fullname[MAX_PATH];
+  char fullname[MAX_UTF8_PATH];
 
   if (fd != AT_FDCWD)
     {
-      if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+      char lastc = dir_pathname[strlen (dir_pathname) - 1];
+
+      if (_snprintf (fullname, sizeof fullname, "%s%s%s",
+                    dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name)
          < 0)
        {
          errno = ENAMETOOLONG;
@@ -4805,6 +5355,30 @@ utime (const char *name, struct utimbuf *times)
   return 0;
 }
 
+int
+sys_umask (int mode)
+{
+  static int current_mask;
+  int retval, arg = 0;
+
+  /* The only bit we really support is the write bit.  Files are
+     always readable on MS-Windows, and the execute bit does not exist
+     at all.  */
+  /* FIXME: if the GROUP and OTHER bits are reset, we should use ACLs
+     to prevent access by other users on NTFS.  */
+  if ((mode & S_IWRITE) != 0)
+    arg |= S_IWRITE;
+
+  retval = _umask (arg);
+  /* Merge into the return value the bits they've set the last time,
+     which msvcrt.dll ignores and never returns.  Emacs insists on its
+     notion of mask being identical to what we return.  */
+  retval |= (current_mask & ~S_IWRITE);
+  current_mask = mode;
+
+  return retval;
+}
+
 \f
 /* Symlink-related functions.  */
 #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
@@ -4814,10 +5388,9 @@ utime (const char *name, struct utimbuf *times)
 int
 symlink (char const *filename, char const *linkname)
 {
-  char linkfn[MAX_PATH], *tgtfn;
+  char linkfn[MAX_UTF8_PATH], *tgtfn;
   DWORD flags = 0;
   int dir_access, filename_ends_in_slash;
-  int dbcs_p;
 
   /* Diagnostics follows Posix as much as possible.  */
   if (filename == NULL || linkname == NULL)
@@ -4830,7 +5403,7 @@ symlink (char const *filename, char const *linkname)
       errno = ENOENT;
       return -1;
     }
-  if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH)
+  if (strlen (filename) > MAX_UTF8_PATH || strlen (linkname) > MAX_UTF8_PATH)
     {
       errno = ENAMETOOLONG;
       return -1;
@@ -4843,8 +5416,6 @@ symlink (char const *filename, char const *linkname)
       return -1;
     }
 
-  dbcs_p = max_filename_mbslen () > 1;
-
   /* Note: since empty FILENAME was already rejected, we can safely
      refer to FILENAME[1].  */
   if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
@@ -4856,24 +5427,11 @@ symlink (char const *filename, char const *linkname)
         directory where the Emacs process runs.  Note that
         make-symbolic-link always makes sure LINKNAME is a fully
         expanded file name.  */
-      char tem[MAX_PATH];
+      char tem[MAX_UTF8_PATH];
       char *p = linkfn + strlen (linkfn);
 
-      if (!dbcs_p)
-       {
-         while (p > linkfn && !IS_ANY_SEP (p[-1]))
-           p--;
-       }
-      else
-       {
-         char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
-
-         while (p > linkfn && !IS_ANY_SEP (*p1))
-           {
-             p = p1;
-             p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
-           }
-       }
+      while (p > linkfn && !IS_ANY_SEP (p[-1]))
+       p--;
       if (p > linkfn)
        strncpy (tem, linkfn, p - linkfn);
       tem[p - linkfn] = '\0';
@@ -4888,15 +5446,7 @@ symlink (char const *filename, char const *linkname)
      exist, but ends in a slash, we create a symlink to directory.  If
      FILENAME exists and is a directory, we always create a symlink to
      directory.  */
-  if (!dbcs_p)
-    filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
-  else
-    {
-      const char *end = filename + strlen (filename);
-      const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
-
-      filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
-    }
+  filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
   if (dir_access == 0 || filename_ends_in_slash)
     flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
 
@@ -4966,10 +5516,23 @@ static int
 is_symlink (const char *filename)
 {
   DWORD attrs;
-  WIN32_FIND_DATA wfd;
+  wchar_t filename_w[MAX_PATH];
+  char filename_a[MAX_PATH];
+  WIN32_FIND_DATAW wfdw;
+  WIN32_FIND_DATAA wfda;
   HANDLE fh;
+  int attrs_mean_symlink;
 
-  attrs = GetFileAttributes (filename);
+  if (w32_unicode_filenames)
+    {
+      filename_to_utf16 (filename, filename_w);
+      attrs = GetFileAttributesW (filename_w);
+    }
+  else
+    {
+      filename_to_ansi (filename, filename_a);
+      attrs = GetFileAttributesA (filename_a);
+    }
   if (attrs == -1)
     {
       DWORD w32err = GetLastError ();
@@ -4992,12 +5555,30 @@ is_symlink (const char *filename)
   if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
     return 0;
   logon_network_drive (filename);
-  fh = FindFirstFile (filename, &wfd);
+  if (w32_unicode_filenames)
+    {
+      fh = FindFirstFileW (filename_w, &wfdw);
+      attrs_mean_symlink =
+       (wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+       && (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+    }
+  else if (_mbspbrk (filename_a, "?"))
+    {
+      /* filename_to_ansi failed to convert the file name.  */
+      errno = ENOENT;
+      return 0;
+    }
+  else
+    {
+      fh = FindFirstFileA (filename_a, &wfda);
+      attrs_mean_symlink =
+       (wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+       && (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+    }
   if (fh == INVALID_HANDLE_VALUE)
     return 0;
   FindClose (fh);
-  return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
-         && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+  return attrs_mean_symlink;
 }
 
 /* If NAME identifies a symbolic link, copy into BUF the file name of
@@ -5014,6 +5595,7 @@ readlink (const char *name, char *buf, size_t buf_size)
   int restore_privs = 0;
   HANDLE sh;
   ssize_t retval;
+  char resolved[MAX_UTF8_PATH];
 
   if (name == NULL)
     {
@@ -5028,7 +5610,7 @@ readlink (const char *name, char *buf, size_t buf_size)
 
   path = map_w32_filename (name, NULL);
 
-  if (strlen (path) > MAX_PATH)
+  if (strlen (path) > MAX_UTF8_PATH)
     {
       errno = ENAMETOOLONG;
       return -1;
@@ -5058,9 +5640,26 @@ readlink (const char *name, char *buf, size_t buf_size)
      e.g. 'C:\Users\All Users', GENERIC_READ fails with
      ERROR_ACCESS_DENIED.  Zero seems to work just fine, both for file
      and directory symlinks.  */
-  sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING,
-                  FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
-                  NULL);
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      sh = CreateFileW (path_w, 0, 0, NULL, OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT
+                       | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      sh = CreateFileA (path_a, 0, 0, NULL, OPEN_EXISTING,
+                       FILE_FLAG_OPEN_REPARSE_POINT
+                       | FILE_FLAG_BACKUP_SEMANTICS,
+                       NULL);
+    }
   if (sh != INVALID_HANDLE_VALUE)
     {
       BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
@@ -5079,89 +5678,27 @@ readlink (const char *name, char *buf, size_t buf_size)
             reparse_data, then convert it to multibyte encoding in
             the current locale's codepage.  */
          WCHAR *lwname;
-         BYTE  lname[MAX_PATH];
-         USHORT lname_len;
+         size_t lname_size;
          USHORT lwname_len =
            reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
          WCHAR *lwname_src =
            reparse_data->SymbolicLinkReparseBuffer.PathBuffer
            + reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
-         /* This updates file_name_codepage which we need below.  */
-         int dbcs_p = max_filename_mbslen () > 1;
+         size_t size_to_copy = buf_size;
 
          /* According to MSDN, PrintNameLength does not include the
             terminating null character.  */
          lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
          memcpy (lwname, lwname_src, lwname_len);
          lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
-
-         lname_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
-                                          lname, MAX_PATH, NULL, NULL);
-         if (!lname_len)
-           {
-             /* WideCharToMultiByte failed.  */
-             DWORD w32err1 = GetLastError ();
-
-             switch (w32err1)
-               {
-               case ERROR_INSUFFICIENT_BUFFER:
-                 errno = ENAMETOOLONG;
-                 break;
-               case ERROR_INVALID_PARAMETER:
-                 errno = EFAULT;
-                 break;
-               case ERROR_NO_UNICODE_TRANSLATION:
-                 errno = ENOENT;
-                 break;
-               default:
-                 errno = EINVAL;
-                 break;
-               }
-           }
-         else
-           {
-             size_t size_to_copy = buf_size;
-             BYTE *p = lname, *p2;
-             BYTE *pend = p + lname_len;
-
-             /* Normalize like dostounix_filename does, but we don't
-                want to assume that lname is null-terminated.  */
-             if (dbcs_p)
-               p2 = CharNextExA (file_name_codepage, p, 0);
-             else
-               p2 = p + 1;
-             if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
-               {
-                 *p += 'a' - 'A';
-                 p += 2;
-               }
-             while (p <= pend)
-               {
-                 if (*p == '\\')
-                   *p = '/';
-                 if (dbcs_p)
-                   {
-                     p = CharNextExA (file_name_codepage, p, 0);
-                     /* CharNextExA doesn't advance at null character.  */
-                     if (!*p)
-                       break;
-                   }
-                 else
-                   ++p;
-               }
-             /* Testing for null-terminated LNAME is paranoia:
-                WideCharToMultiByte should always return a
-                null-terminated string when its 4th argument is -1
-                and its 3rd argument is null-terminated (which they
-                are, see above).  */
-             if (lname[lname_len - 1] == '\0')
-               lname_len--;
-             if (lname_len <= buf_size)
-               size_to_copy = lname_len;
-             strncpy (buf, lname, size_to_copy);
-             /* Success!  */
-             retval = size_to_copy;
-           }
+         filename_from_utf16 (lwname, resolved);
+         dostounix_filename (resolved);
+         lname_size = strlen (resolved) + 1;
+         if (lname_size <= buf_size)
+           size_to_copy = lname_size;
+         strncpy (buf, resolved, size_to_copy);
+         /* Success!  */
+         retval = size_to_copy;
        }
       CloseHandle (sh);
     }
@@ -5200,7 +5737,7 @@ readlinkat (int fd, char const *name, char *buffer,
 {
   /* Rely on a hack: an open directory is modeled as file descriptor 0,
      as in fstatat.  FIXME: Add proper support for readlinkat.  */
-  char fullname[MAX_PATH];
+  char fullname[MAX_UTF8_PATH];
 
   if (fd != AT_FDCWD)
     {
@@ -5239,41 +5776,45 @@ readlinkat (int fd, char const *name, char *buffer,
 static char *
 chase_symlinks (const char *file)
 {
-  static char target[MAX_PATH];
-  char link[MAX_PATH];
+  static char target[MAX_UTF8_PATH];
+  char link[MAX_UTF8_PATH];
+  wchar_t target_w[MAX_PATH], link_w[MAX_PATH];
+  char target_a[MAX_PATH], link_a[MAX_PATH];
   ssize_t res, link_len;
   int loop_count = 0;
-  int dbcs_p;
 
   if (is_windows_9x () == TRUE || !is_symlink (file))
     return (char *)file;
 
-  if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
-    return (char *)file;
+  if (w32_unicode_filenames)
+    {
+      wchar_t file_w[MAX_PATH];
+
+      filename_to_utf16 (file, file_w);
+      if (GetFullPathNameW (file_w, MAX_PATH, link_w, NULL) == 0)
+       return (char *)file;
+      filename_from_utf16 (link_w, link);
+    }
+  else
+    {
+      char file_a[MAX_PATH];
+
+      filename_to_ansi (file, file_a);
+      if (GetFullPathNameA (file_a, MAX_PATH, link_a, NULL) == 0)
+       return (char *)file;
+      filename_from_ansi (link_a, link);
+    }
+  link_len = strlen (link);
 
-  dbcs_p = max_filename_mbslen () > 1;
   target[0] = '\0';
   do {
 
     /* Remove trailing slashes, as we want to resolve the last
        non-trivial part of the link name.  */
-    if (!dbcs_p)
-      {
-       while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
-         link[link_len--] = '\0';
-      }
-    else if (link_len > 3)
-      {
-       char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
+    while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+      link[link_len--] = '\0';
 
-       while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
-         {
-           n[1] = '\0';
-           n = CharPrevExA (file_name_codepage, link, n, 0);
-         }
-      }
-
-    res = readlink (link, target, MAX_PATH);
+    res = readlink (link, target, MAX_UTF8_PATH);
     if (res > 0)
       {
        target[res] = '\0';
@@ -5284,27 +5825,28 @@ chase_symlinks (const char *file)
               the symlink, then copy the result back to target.  */
            char *p = link + link_len;
 
-           if (!dbcs_p)
-             {
-               while (p > link && !IS_ANY_SEP (p[-1]))
-                 p--;
-             }
-           else
-             {
-               char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
-
-               while (p > link && !IS_ANY_SEP (*p1))
-                 {
-                   p = p1;
-                   p1 = CharPrevExA (file_name_codepage, link, p1, 0);
-                 }
-             }
+           while (p > link && !IS_ANY_SEP (p[-1]))
+             p--;
            strcpy (p, target);
            strcpy (target, link);
          }
        /* Resolve any "." and ".." to get a fully-qualified file name
           in link[] again. */
-       link_len = GetFullPathName (target, MAX_PATH, link, NULL);
+       if (w32_unicode_filenames)
+         {
+           filename_to_utf16 (target, target_w);
+           link_len = GetFullPathNameW (target_w, MAX_PATH, link_w, NULL);
+           if (link_len > 0)
+             filename_from_utf16 (link_w, link);
+         }
+       else
+         {
+           filename_to_ansi (target, target_a);
+           link_len = GetFullPathNameA (target_a, MAX_PATH, link_a, NULL);
+           if (link_len > 0)
+             filename_from_ansi (link_a, link);
+         }
+       link_len = strlen (link);
       }
   } while (res > 0 && link_len > 0 && ++loop_count <= 100);
 
@@ -5421,7 +5963,11 @@ acl_get_file (const char *fname, acl_type_t type)
                }
            }
          else if (err == ERROR_FILE_NOT_FOUND
-                  || err == ERROR_PATH_NOT_FOUND)
+                  || err == ERROR_PATH_NOT_FOUND
+                  /* ERROR_INVALID_NAME is what we get if
+                     w32-unicode-filenames is nil and the file cannot
+                     be encoded in the current ANSI codepage. */
+                  || err == ERROR_INVALID_NAME)
            errno = ENOENT;
          else
            errno = EIO;
@@ -5442,7 +5988,7 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
   DWORD err;
   int st = 0, retval = -1;
   SECURITY_INFORMATION flags = 0;
-  PSID psid;
+  PSID psidOwner, psidGroup;
   PACL pacl;
   BOOL dflt;
   BOOL dacl_present;
@@ -5468,11 +6014,13 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
   else
     fname = filename;
 
-  if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
-      && psid)
+  if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psidOwner,
+                                    &dflt)
+      && psidOwner)
     flags |= OWNER_SECURITY_INFORMATION;
-  if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
-      && psid)
+  if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psidGroup,
+                                    &dflt)
+      && psidGroup)
     flags |= GROUP_SECURITY_INFORMATION;
   if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
                                    &pacl, &dflt)
@@ -5499,10 +6047,22 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
 
   e = errno;
   errno = 0;
-  if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
+  /* SetFileSecurity is deprecated by MS, and sometimes fails when
+     DACL inheritance is involved, but it seems to preserve ownership
+     better than SetNamedSecurityInfo, which is important e.g., in
+     copy-file.  */
+  if (!set_file_security (fname, flags, (PSECURITY_DESCRIPTOR)acl))
     {
       err = GetLastError ();
 
+      if (errno != ENOTSUP)
+       err = set_named_security_info (fname, SE_FILE_OBJECT, flags,
+                                      psidOwner, psidGroup, pacl, NULL);
+    }
+  else
+    err = ERROR_SUCCESS;
+  if (err != ERROR_SUCCESS)
+    {
       if (errno == ENOTSUP)
        ;
       else if (err == ERROR_INVALID_OWNER
@@ -5532,7 +6092,12 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl)
              acl_free (current_acl);
            }
        }
-      else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+      else if (err == ERROR_FILE_NOT_FOUND
+              || err == ERROR_PATH_NOT_FOUND
+              /* ERROR_INVALID_NAME is what we get if
+                 w32-unicode-filenames is nil and the file cannot be
+                 encoded in the current ANSI codepage. */
+              || err == ERROR_INVALID_NAME)
        errno = ENOENT;
       else
        errno = EACCES;
@@ -5565,7 +6130,7 @@ careadlinkat (int fd, char const *filename,
               struct allocator const *alloc,
               ssize_t (*preadlinkat) (int, char const *, char *, size_t))
 {
-  char linkname[MAX_PATH];
+  char linkname[MAX_UTF8_PATH];
   ssize_t link_size;
 
   link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
@@ -5585,6 +6150,116 @@ careadlinkat (int fd, char const *filename,
   return NULL;
 }
 
+int
+w32_copy_file (const char *from, const char *to,
+              int keep_time, int preserve_ownership, int copy_acls)
+{
+  acl_t acl = NULL;
+  BOOL copy_result;
+  wchar_t from_w[MAX_PATH], to_w[MAX_PATH];
+  char from_a[MAX_PATH], to_a[MAX_PATH];
+
+  /* We ignore preserve_ownership for now.  */
+  preserve_ownership = preserve_ownership;
+
+  if (copy_acls)
+    {
+      acl = acl_get_file (from, ACL_TYPE_ACCESS);
+      if (acl == NULL && acl_errno_valid (errno))
+       return -2;
+    }
+  if (w32_unicode_filenames)
+    {
+      filename_to_utf16 (from, from_w);
+      filename_to_utf16 (to, to_w);
+      copy_result = CopyFileW (from_w, to_w, FALSE);
+    }
+  else
+    {
+      filename_to_ansi (from, from_a);
+      filename_to_ansi (to, to_a);
+      copy_result = CopyFileA (from_a, to_a, FALSE);
+    }
+  if (!copy_result)
+    {
+      /* CopyFile doesn't set errno when it fails.  By far the most
+        "popular" reason is that the target is read-only.  */
+      DWORD err = GetLastError ();
+
+      switch (err)
+       {
+       case ERROR_FILE_NOT_FOUND:
+         errno = ENOENT;
+         break;
+       case ERROR_ACCESS_DENIED:
+         errno = EACCES;
+         break;
+       case ERROR_ENCRYPTION_FAILED:
+         errno = EIO;
+         break;
+       default:
+         errno = EPERM;
+         break;
+       }
+
+      if (acl)
+       acl_free (acl);
+      return -1;
+    }
+  /* CopyFile retains the timestamp by default.  However, see
+     "Community Additions" for CopyFile: it sounds like that is not
+     entirely true.  Testing on Windows XP confirms that modified time
+     is copied, but creation and last-access times are not.
+     FIXME?  */
+  else if (!keep_time)
+    {
+      struct timespec now;
+      DWORD attributes;
+
+      if (w32_unicode_filenames)
+       {
+         /* Ensure file is writable while its times are set.  */
+         attributes = GetFileAttributesW (to_w);
+         SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY);
+         now = current_timespec ();
+         if (set_file_times (-1, to, now, now))
+           {
+             /* Restore original attributes.  */
+             SetFileAttributesW (to_w, attributes);
+             if (acl)
+               acl_free (acl);
+             return -3;
+           }
+         /* Restore original attributes.  */
+         SetFileAttributesW (to_w, attributes);
+       }
+      else
+       {
+         attributes = GetFileAttributesA (to_a);
+         SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY);
+         now = current_timespec ();
+         if (set_file_times (-1, to, now, now))
+           {
+             SetFileAttributesA (to_a, attributes);
+             if (acl)
+               acl_free (acl);
+             return -3;
+           }
+         SetFileAttributesA (to_a, attributes);
+       }
+    }
+  if (acl != NULL)
+    {
+      bool fail =
+       acl_set_file (to, ACL_TYPE_ACCESS, acl) != 0;
+      acl_free (acl);
+      if (fail && acl_errno_valid (errno))
+       return -4;
+    }
+
+  return 0;
+}
+
 \f
 /* Support for browsing other processes and their attributes.  See
    process.c for the Lisp bindings.  */
@@ -7836,7 +8511,7 @@ network_interface_get_info (Lisp_Object ifname)
 
              /* Hardware address and its family.  */
              for (n = 0; n < adapter->AddressLength; n++)
-               p->u.contents[n] = make_number ((int) adapter->Address[n]);
+               p->contents[n] = make_number ((int) adapter->Address[n]);
              /* Windows does not support AF_LINK or AF_PACKET family
                 of addresses.  Use an arbitrary family number that is
                 identical to what GNU/Linux returns.  */
@@ -7968,7 +8643,7 @@ sys_localtime (const time_t *t)
 HMODULE
 w32_delayed_load (Lisp_Object library_id)
 {
-  HMODULE library_dll = NULL;
+  HMODULE dll_handle = NULL;
 
   CHECK_SYMBOL (library_id);
 
@@ -7981,26 +8656,63 @@ w32_delayed_load (Lisp_Object library_id)
       if (CONSP (dlls))
         for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
           {
-            CHECK_STRING_CAR (dlls);
-            if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
-              {
-                char name[MAX_PATH];
-                DWORD len;
-
-                len = GetModuleFileNameA (library_dll, name, sizeof (name));
-                found = Fcons (XCAR (dlls),
-                               (len > 0)
-                               /* Possibly truncated */
-                               ? make_specified_string (name, -1, len, 1)
-                               : Qnil);
-                break;
-              }
-          }
+           Lisp_Object dll = XCAR (dlls);
+           char name[MAX_UTF8_PATH];
+           DWORD res = -1;
+
+           CHECK_STRING (dll);
+           dll = ENCODE_FILE (dll);
+           if (w32_unicode_filenames)
+             {
+               wchar_t name_w[MAX_PATH];
+
+               filename_to_utf16 (SSDATA (dll), name_w);
+               dll_handle = LoadLibraryW (name_w);
+               if (dll_handle)
+                 {
+                   res = GetModuleFileNameW (dll_handle, name_w,
+                                             sizeof (name_w));
+                   if (res > 0)
+                     filename_from_utf16 (name_w, name);
+                 }
+             }
+           else
+             {
+               char name_a[MAX_PATH];
+
+               filename_to_ansi (SSDATA (dll), name_a);
+               dll_handle = LoadLibraryA (name_a);
+               if (dll_handle)
+                 {
+                   res = GetModuleFileNameA (dll_handle, name_a,
+                                             sizeof (name_a));
+                   if (res > 0)
+                     filename_from_ansi (name_a, name);
+                 }
+             }
+           if (dll_handle)
+             {
+               ptrdiff_t len = strlen (name);
+               found = Fcons (dll,
+                              (res > 0)
+                              /* Possibly truncated */
+                              ? make_specified_string (name, -1, len, 1)
+                              : Qnil);
+               /* This prevents thread start and end notifications
+                  from being sent to the DLL, for every thread we
+                  start.  We don't need those notifications because
+                  threads we create never use any of these DLLs, only
+                  the main thread uses them.  This is supposed to
+                  speed up thread creation.  */
+               DisableThreadLibraryCalls (dll_handle);
+               break;
+             }
+         }
 
       Fput (library_id, QCloaded_from, found);
     }
 
-  return library_dll;
+  return dll_handle;
 }
 
 \f
@@ -8019,8 +8731,14 @@ check_windows_init_file (void)
       Lisp_Object init_file;
       int fd;
 
+      /* Implementation note: this function runs early during Emacs
+        startup, before startup.el is run.  So Vload_path is still in
+        its initial unibyte form, but it holds UTF-8 encoded file
+        names, since init_callproc was already called.  So we do not
+        need to ENCODE_FILE here, but we do need to convert the file
+        names from UTF-8 to ANSI.  */
       init_file = build_string ("term/w32-win");
-      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
+      fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0);
       if (fd < 0)
        {
          Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
@@ -8029,6 +8747,8 @@ check_windows_init_file (void)
          char *buffer = alloca (1024
                                 + strlen (init_file_name)
                                 + strlen (load_path));
+         char *msg = buffer;
+         int needed;
 
          sprintf (buffer,
                   "The Emacs Windows initialization file \"%s.el\" "
@@ -8040,8 +8760,27 @@ check_windows_init_file (void)
                   "not unpacked properly.\nSee the README.W32 file in the "
                   "top-level Emacs directory for more information.",
                   init_file_name, load_path);
+         needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer,
+                                        -1, NULL, 0);
+         if (needed > 0)
+           {
+             wchar_t *msg_w = alloca ((needed + 1) * sizeof (wchar_t));
+
+             pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1,
+                                   msg_w, needed);
+             needed = pWideCharToMultiByte (CP_ACP, 0, msg_w, -1,
+                                            NULL, 0, NULL, NULL);
+             if (needed > 0)
+               {
+                 char *msg_a = alloca (needed + 1);
+
+                 pWideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
+                                       NULL, NULL);
+                 msg = msg_a;
+               }
+           }
          MessageBox (NULL,
-                     buffer,
+                     msg,
                      "Emacs Abort Dialog",
                      MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
          /* Use the low-level system abort. */
@@ -8195,6 +8934,57 @@ shutdown_handler (DWORD type)
   return FALSE;
 }
 
+/* On Windows 9X, load UNICOWS.DLL and return its handle, or die.  On
+   NT, return a handle to GDI32.DLL.  */
+HANDLE
+maybe_load_unicows_dll (void)
+{
+  if (os_subtype == OS_9X)
+    {
+      HANDLE ret = LoadLibrary ("Unicows.dll");
+      if (ret)
+       {
+         /* These two functions are present on Windows 9X as stubs
+            that always fail.  We need the real implementations from
+            UNICOWS.DLL, so we must call these functions through
+            pointers, and assign the correct addresses to these
+            pointers at program startup (see emacs.c, which calls
+            this function early on).  */
+         pMultiByteToWideChar = GetProcAddress (ret, "MultiByteToWideChar");
+         pWideCharToMultiByte = GetProcAddress (ret, "WideCharToMultiByte");
+         return ret;
+       }
+      else
+       {
+         int button;
+
+         button = MessageBox (NULL,
+                              "Emacs cannot load the UNICOWS.DLL library.\n"
+                              "This library is essential for using Emacs\n"
+                              "on this system.  You need to install it.\n\n"
+                              "Emacs will exit when you click OK.",
+                              "Emacs cannot load UNICOWS.DLL",
+                              MB_ICONERROR | MB_TASKMODAL
+                              | MB_SETFOREGROUND | MB_OK);
+         switch (button)
+           {
+           case IDOK:
+           default:
+             exit (1);
+           }
+       }
+    }
+  else
+    {
+      /* On NT family of Windows, these two functions are always
+        linked in, so we just assign their addresses to the 2
+        pointers; no need for the LoadLibrary dance.  */
+      pMultiByteToWideChar = MultiByteToWideChar;
+      pWideCharToMultiByte = WideCharToMultiByte;
+      return LoadLibrary ("Gdi32.dll");
+    }
+}
+
 /*
        globals_of_w32 is used to initialize those global variables that
        must always be initialized on startup even when the global variable
@@ -8217,7 +9007,8 @@ globals_of_w32 (void)
   g_b_init_get_sid_sub_authority = 0;
   g_b_init_get_sid_sub_authority_count = 0;
   g_b_init_get_security_info = 0;
-  g_b_init_get_file_security = 0;
+  g_b_init_get_file_security_w = 0;
+  g_b_init_get_file_security_a = 0;
   g_b_init_get_security_descriptor_owner = 0;
   g_b_init_get_security_descriptor_group = 0;
   g_b_init_is_valid_sid = 0;
@@ -8236,12 +9027,16 @@ globals_of_w32 (void)
   g_b_init_get_length_sid = 0;
   g_b_init_get_native_system_info = 0;
   g_b_init_get_system_times = 0;
-  g_b_init_create_symbolic_link = 0;
+  g_b_init_create_symbolic_link_w = 0;
+  g_b_init_create_symbolic_link_a = 0;
   g_b_init_get_security_descriptor_dacl = 0;
   g_b_init_convert_sd_to_sddl = 0;
   g_b_init_convert_sddl_to_sd = 0;
   g_b_init_is_valid_security_descriptor = 0;
-  g_b_init_set_file_security = 0;
+  g_b_init_set_file_security_w = 0;
+  g_b_init_set_file_security_a = 0;
+  g_b_init_set_named_security_info_w = 0;
+  g_b_init_set_named_security_info_a = 0;
   g_b_init_get_adapters_info = 0;
   num_of_processors = 0;
   /* The following sets a handler for shutdown notifications for
@@ -8462,8 +9257,6 @@ ssize_t
 emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
 {
   int n, err;
-  SELECT_TYPE fdset;
-  struct timespec timeout;
   struct Lisp_Process *process = (struct Lisp_Process *)p;
   int fd = process->infd;