sys_link works, including enhanced error handling.
[bpt/emacs.git] / src / w32.c
index 7d63c73..46f36de 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -19,6 +19,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 /*
    Geoff Voelker (voelker@cs.washington.edu)                         7-29-94
 */
+
+#include <mingw_time.h>
 #include <stddef.h> /* for offsetof */
 #include <stdlib.h>
 #include <stdio.h>
@@ -47,7 +49,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #undef fopen
 #undef link
 #undef mkdir
-#undef mktemp
 #undef open
 #undef rename
 #undef rmdir
@@ -90,6 +91,21 @@ typedef struct _MEMORY_STATUS_EX {
   DWORDLONG ullAvailExtendedVirtual;
 } MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
 
+/* These are here so that GDB would know about these data types.  This
+   allows to attach GDB to Emacs when a fatal exception is triggered
+   and Windows pops up the "application needs to be closed" dialog.
+   At that point, _gnu_exception_handler, the top-level exception
+   handler installed by the MinGW startup code, is somewhere on the
+   call-stack of the main thread, so going to that call frame and
+   looking at the argument to _gnu_exception_handler, which is a
+   PEXCEPTION_POINTERS pointer, can reveal the exception code
+   (excptr->ExceptionRecord->ExceptionCode) and the address where the
+   exception happened (excptr->ExceptionRecord->ExceptionAddress), as
+   well as some additional information specific to the exception.  */
+PEXCEPTION_POINTERS excptr;
+PEXCEPTION_RECORD excprec;
+PCONTEXT ctxrec;
+
 #include <lmcons.h>
 #include <shlobj.h>
 
@@ -202,6 +218,8 @@ typedef struct _REPARSE_DATA_BUFFER {
 #undef recvfrom
 #undef sendto
 
+#include <iphlpapi.h>  /* should be after winsock2.h */
+
 #include "w32.h"
 #include <dirent.h>
 #include "w32common.h"
@@ -230,10 +248,12 @@ static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *);
 static int restore_privilege (TOKEN_PRIVILEGES *);
 static BOOL WINAPI revert_to_self (void);
 
-extern int sys_access (const char *, int);
+static int sys_access (const char *, int);
 extern void *e_malloc (size_t);
 extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *,
-                      EMACS_TIME *, void *);
+                      struct timespec *, void *);
+extern int sys_dup (int);
+
 
 
 \f
@@ -278,6 +298,7 @@ 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_get_adapters_info;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -420,6 +441,9 @@ typedef BOOL (WINAPI *ConvertSecurityDescriptorToStringSecurityDescriptor_Proc)
     LPTSTR  *StringSecurityDescriptor,
     PULONG StringSecurityDescriptorLen);
 typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR);
+typedef DWORD (WINAPI *GetAdaptersInfo_Proc) (
+    PIP_ADAPTER_INFO pAdapterInfo,
+    PULONG pOutBufLen);
 
   /* ** A utility function ** */
 static BOOL
@@ -1112,6 +1136,28 @@ convert_sddl_to_sd (LPCTSTR StringSecurityDescriptor,
   return retval;
 }
 
+static DWORD WINAPI
+get_adapters_info (PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen)
+{
+  static GetAdaptersInfo_Proc s_pfn_Get_Adapters_Info = NULL;
+  HMODULE hm_iphlpapi = NULL;
+
+  if (is_windows_9x () == TRUE)
+    return ERROR_NOT_SUPPORTED;
+
+  if (g_b_init_get_adapters_info == 0)
+    {
+      g_b_init_get_adapters_info = 1;
+      hm_iphlpapi = LoadLibrary ("Iphlpapi.dll");
+      if (hm_iphlpapi)
+       s_pfn_Get_Adapters_Info = (GetAdaptersInfo_Proc)
+         GetProcAddress (hm_iphlpapi, "GetAdaptersInfo");
+    }
+  if (s_pfn_Get_Adapters_Info == NULL)
+    return ERROR_NOT_SUPPORTED;
+  return s_pfn_Get_Adapters_Info (pAdapterInfo, pOutBufLen);
+}
+
 \f
 
 /* Return 1 if P is a valid pointer to an object of size SIZE.  Return
@@ -1136,7 +1182,193 @@ w32_valid_pointer_p (void *p, int size)
     return -1;
 }
 
-static char startup_dir[MAXPATHLEN];
+\f
+
+/* Converting file names from UTF-8 to either UTF-16 or the ANSI
+   codepage defined by file-name-coding-system.  */
+
+/* Current codepage for encoding file names.  */
+static int file_name_codepage;
+
+/* Produce a Windows ANSI codepage suitable for encoding file names.
+   Return the information about that codepage in CP_INFO.  */
+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;
+}
+
+static 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);
+
+  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;
+}
+
+static 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);
+
+  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;
+}
+
+static 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  = WideCharToMultiByte (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 = MultiByteToWideChar (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);
+}
+
+\f
+
+/* The directory where we started, in UTF-8. */
+static char startup_dir[MAX_UTF8_PATH];
 
 /* Get the current working directory.  */
 char *
@@ -1328,8 +1560,8 @@ getloadavg (double loadavg[], int nelem)
 static char dflt_passwd_name[PASSWD_FIELD_SIZE];
 static char dflt_passwd_passwd[PASSWD_FIELD_SIZE];
 static char dflt_passwd_gecos[PASSWD_FIELD_SIZE];
-static char dflt_passwd_dir[PASSWD_FIELD_SIZE];
-static char dflt_passwd_shell[PASSWD_FIELD_SIZE];
+static char dflt_passwd_dir[MAX_UTF8_PATH];
+static char dflt_passwd_shell[MAX_UTF8_PATH];
 
 static struct passwd dflt_passwd =
 {
@@ -1510,15 +1742,32 @@ init_user_info (void)
     }
   dflt_group.gr_gid = dflt_passwd.pw_gid;
 
-  /* Ensure HOME and SHELL are defined. */
-  if (getenv ("HOME") == NULL)
-    emacs_abort ();
-  if (getenv ("SHELL") == NULL)
-    emacs_abort ();
-
   /* Set dir and shell from environment variables. */
-  strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
-  strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
+  if (w32_unicode_filenames)
+    {
+      wchar_t *home = _wgetenv (L"HOME");
+      wchar_t *shell = _wgetenv (L"SHELL");
+
+      /* Ensure HOME and SHELL are defined. */
+      if (home == NULL)
+       emacs_abort ();
+      if (shell == NULL)
+       emacs_abort ();
+      filename_from_utf16 (home, dflt_passwd.pw_dir);
+      filename_from_utf16 (shell, dflt_passwd.pw_shell);
+    }
+  else
+    {
+      char *home = getenv ("HOME");
+      char *shell = getenv ("SHELL");
+
+      if (home == NULL)
+       emacs_abort ();
+      if (shell == NULL)
+       emacs_abort ();
+      filename_from_ansi (home, dflt_passwd.pw_dir);
+      filename_from_ansi (shell, dflt_passwd.pw_shell);
+    }
 
   xfree (buf);
   if (token)
@@ -1538,93 +1787,32 @@ srandom (int seed)
   srand (seed);
 }
 
-/* Current codepage for encoding file names.  */
-static int file_name_codepage;
-
 /* Return the maximum length in bytes of a multibyte character
    sequence encoded in the current ANSI codepage.  This is required to
    correctly walk the encoded file names one character at a time.  */
 static int
 max_filename_mbslen (void)
 {
-  /* A simple cache to avoid calling GetCPInfo every time we need to
-     normalize 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 int last_max_mbslen;
-  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))
-    {
-      CPINFO cp_info;
+  CPINFO cp_info;
 
-      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_info))
-       {
-         file_name_codepage = CP_ACP;
-         if (!GetCPInfo (file_name_codepage, &cp_info))
-           emacs_abort ();
-       }
-      last_max_mbslen = cp_info.MaxCharSize;
-    }
-
-  return last_max_mbslen;
+  codepage_for_filenames (&cp_info);
+  return cp_info.MaxCharSize;
 }
 
-/* Normalize filename by converting all path separators to
-   the specified separator.  Also conditionally convert upper
-   case path name components to lower case.  */
+/* Normalize filename by converting in-place all of its path
+   separators to the separator specified by PATH_SEP.  */
 
 static void
-normalize_filename (register char *fp, char path_sep, int multibyte)
+normalize_filename (register char *fp, char path_sep)
 {
-  char sep;
-  char *elem, *p2;
-  int dbcs_p = max_filename_mbslen () > 1;
-
-  /* Multibyte file names are in the Emacs internal representation, so
-     we can traverse them by bytes with no problems.  */
-  if (multibyte)
-    dbcs_p = 0;
+  char *p2;
 
   /* Always lower-case drive letters a-z, even if the filesystem
      preserves case in filenames.
      This is so filenames can be compared by string comparison
      functions that are case-sensitive.  Even case-preserving filesystems
      do not distinguish case in drive letters.  */
-  if (dbcs_p)
-    p2 = CharNextExA (file_name_codepage, fp, 0);
-  else
-    p2 = fp + 1;
+  p2 = fp + 1;
 
   if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
     {
@@ -1632,68 +1820,26 @@ normalize_filename (register char *fp, char path_sep, int multibyte)
       fp += 2;
     }
 
-  if (multibyte || NILP (Vw32_downcase_file_names))
+  while (*fp)
     {
-      while (*fp)
-       {
-         if (*fp == '/' || *fp == '\\')
-           *fp = path_sep;
-         if (!dbcs_p)
-           fp++;
-         else
-           fp = CharNextExA (file_name_codepage, fp, 0);
-       }
-      return;
+      if (*fp == '/' || *fp == '\\')
+       *fp = path_sep;
+      fp++;
     }
-
-  sep = path_sep;              /* convert to this path separator */
-  elem = fp;                   /* start of current path element */
-
-  do {
-    if (*fp >= 'a' && *fp <= 'z')
-      elem = 0;                        /* don't convert this element */
-
-    if (*fp == 0 || *fp == ':')
-      {
-       sep = *fp;              /* restore current separator (or 0) */
-       *fp = '/';              /* after conversion of this element */
-      }
-
-    if (*fp == '/' || *fp == '\\')
-      {
-       if (elem && elem != fp)
-         {
-           *fp = 0;            /* temporary end of string */
-           _mbslwr (elem);     /* while we convert to lower case */
-         }
-       *fp = sep;              /* convert (or restore) path separator */
-       elem = fp + 1;          /* next element starts after separator */
-       sep = path_sep;
-      }
-    if (*fp)
-      {
-       if (!dbcs_p)
-         fp++;
-       else
-         fp = CharNextExA (file_name_codepage, fp, 0);
-      }
-  } while (*fp);
 }
 
-/* Destructively turn backslashes into slashes.  MULTIBYTE non-zero
-   means the file name is a multibyte string in Emacs's internal
-   representation.  */
+/* Destructively turn backslashes into slashes.  */
 void
-dostounix_filename (register char *p, int multibyte)
+dostounix_filename (register char *p)
 {
-  normalize_filename (p, '/', multibyte);
+  normalize_filename (p, '/');
 }
 
 /* Destructively turn slashes into backslashes.  */
 void
 unixtodos_filename (register char *p)
 {
-  normalize_filename (p, '\\', 0);
+  normalize_filename (p, '\\');
 }
 
 /* Remove all CR's that are followed by a LF.
@@ -1726,9 +1872,9 @@ crlf_to_lf (register int n, register unsigned char *buf)
 /* Parse the root part of file name, if present.  Return length and
     optionally store pointer to char after root.  */
 static int
-parse_root (char * name, char ** pPath)
+parse_root (const char * name, const char ** pPath)
 {
-  char * start = name;
+  const char * start = name;
 
   if (name == NULL)
     return 0;
@@ -1744,17 +1890,13 @@ parse_root (char * name, char ** pPath)
   else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
     {
       int slashes = 2;
-      int dbcs_p = max_filename_mbslen () > 1;
 
       name += 2;
       do
         {
          if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
            break;
-         if (dbcs_p)
-           name = CharNextExA (file_name_codepage, name, 0);
-         else
-           name++;
+         name++;
        }
       while ( *name );
       if (IS_DIRECTORY_SEP (name[0]))
@@ -1771,23 +1913,44 @@ parse_root (char * name, char ** pPath)
 static int
 get_long_basename (char * name, char * buf, int size)
 {
-  WIN32_FIND_DATA find_data;
   HANDLE dir_handle;
+  char fname_utf8[MAX_UTF8_PATH];
   int len = 0;
+  int cstatus;
 
-  /* must be valid filename, no wild cards or other invalid characters */
-  if (_mbspbrk (name, "*?|<>\""))
+  /* Must be valid filename, no wild cards or other invalid characters.  */
+  if (strpbrk (name, "*?|<>\""))
     return 0;
 
-  dir_handle = FindFirstFile (name, &find_data);
-  if (dir_handle != INVALID_HANDLE_VALUE)
+  if (w32_unicode_filenames)
     {
-      if ((len = strlen (find_data.cFileName)) < size)
-       memcpy (buf, find_data.cFileName, len + 1);
-      else
-       len = 0;
-      FindClose (dir_handle);
+      wchar_t fname_utf16[MAX_PATH];
+      WIN32_FIND_DATAW find_data_wide;
+
+      filename_to_utf16 (name, fname_utf16);
+      dir_handle = FindFirstFileW (fname_utf16, &find_data_wide);
+      if (dir_handle != INVALID_HANDLE_VALUE)
+       cstatus = filename_from_utf16 (find_data_wide.cFileName, fname_utf8);
     }
+  else
+    {
+      char fname_ansi[MAX_PATH];
+      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 (cstatus == 0 && (len = strlen (fname_utf8)) < size)
+    memcpy (buf, fname_utf8, len + 1);
+  else
+    len = 0;
+
+  if (dir_handle != INVALID_HANDLE_VALUE)
+    FindClose (dir_handle);
+
   return len;
 }
 
@@ -1797,12 +1960,12 @@ w32_get_long_filename (char * name, char * buf, int size)
 {
   char * o = buf;
   char * p;
-  char * q;
-  char full[ MAX_PATH ];
+  const char * q;
+  char full[ MAX_UTF8_PATH ];
   int len;
 
   len = strlen (name);
-  if (len >= MAX_PATH)
+  if (len >= MAX_UTF8_PATH)
     return FALSE;
 
   /* Use local copy for destructive modification.  */
@@ -1810,7 +1973,7 @@ w32_get_long_filename (char * name, char * buf, int size)
   unixtodos_filename (full);
 
   /* Copy root part verbatim.  */
-  len = parse_root (full, &p);
+  len = parse_root (full, (const char **)&p);
   memcpy (o, full, len);
   o += len;
   *o = '\0';
@@ -1819,7 +1982,7 @@ w32_get_long_filename (char * name, char * buf, int size)
   while (p != NULL && *p)
     {
       q = p;
-      p = _mbschr (q, '\\');
+      p = strchr (q, '\\');
       if (p) *p = '\0';
       len = get_long_basename (full, o, size);
       if (len > 0)
@@ -1843,6 +2006,29 @@ w32_get_long_filename (char * name, char * buf, int size)
   return TRUE;
 }
 
+unsigned int
+w32_get_short_filename (char * name, char * buf, int size)
+{
+  if (w32_unicode_filenames)
+    {
+      wchar_t name_utf16[MAX_PATH], short_name[MAX_PATH];
+      unsigned int retval;
+
+      filename_to_utf16 (name, name_utf16);
+      retval = GetShortPathNameW (name_utf16, short_name, size);
+      if (retval && retval < size)
+       filename_from_utf16 (short_name, buf);
+      return retval;
+    }
+  else
+    {
+      char name_ansi[MAX_PATH];
+
+      filename_to_ansi (name, name_ansi);
+      return GetShortPathNameA (name_ansi, buf, size);
+    }
+}
+
 static int
 is_unc_volume (const char *filename)
 {
@@ -1851,7 +2037,7 @@ is_unc_volume (const char *filename)
   if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
     return 0;
 
-  if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
+  if (strpbrk (ptr + 2, "*?|<>\"\\/"))
     return 0;
 
   return 1;
@@ -1951,8 +2137,6 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
   return (NULL);
 }
 
-char *get_emacs_configuration (void);
-
 void
 init_environment (char ** argv)
 {
@@ -1964,6 +2148,13 @@ init_environment (char ** argv)
 
   const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
 
+  /* Implementation note: This function explicitly works with ANSI
+     file names, not with UTF-8 encoded file names.  This is because
+     this function pushes variables into the Emacs's environment, and
+     the environment variables are always assumed to be in the
+     locale-specific encoding.  Do NOT call any functions that accept
+     UTF-8 file names from this function!  */
+
   /* Make sure they have a usable $TMPDIR.  Many Emacs functions use
      temporary files and assume "/tmp" if $TMPDIR is unset, which
      will break on DOS/Windows.  Refuse to work if we cannot find
@@ -1979,8 +2170,8 @@ init_environment (char ** argv)
         The only way to be really sure is to actually create a file and
         see if it succeeds.  But I think that's too much to ask.  */
 
-      /* MSVCRT's _access crashes with D_OK.  */
-      if (tmp && faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0)
+      /* MSVCRT's _access crashes with D_OK, so we use our replacement.  */
+      if (tmp && sys_access (tmp, D_OK) == 0)
        {
          char * var = alloca (strlen (tmp) + 8);
          sprintf (var, "TMPDIR=%s", tmp);
@@ -2042,7 +2233,7 @@ init_environment (char ** argv)
     /* For backwards compatibility, check if a .emacs file exists in C:/
        If not, then we can try to default to the appdata directory under the
        user's profile, which is more likely to be writable.   */
-    if (!check_existing ("C:/.emacs"))
+    if (sys_access ("C:/.emacs", F_OK) != 0)
       {
        HRESULT profile_result;
        /* Dynamically load ShGetFolderPath, as it won't exist on versions
@@ -2118,33 +2309,6 @@ init_environment (char ** argv)
              _putenv (strdup (buf));
            }
        }
-      /* Handle running emacs from the build directory: src/oo-spd/i386/  */
-
-      /* FIXME: should use substring of get_emacs_configuration ().
-        But I don't think the Windows build supports alpha, mips etc
-         anymore, so have taken the easy option for now.  */
-      else if (p && (xstrcasecmp (p, "\\i386") == 0
-                     || xstrcasecmp (p, "\\AMD64") == 0))
-       {
-         *p = 0;
-         p = _mbsrchr (modname, '\\');
-         if (p != NULL)
-           {
-             *p = 0;
-             p = _mbsrchr (modname, '\\');
-             if (p && xstrcasecmp (p, "\\src") == 0)
-               {
-                 char buf[SET_ENV_BUF_SIZE];
-
-                 *p = 0;
-                 for (p = modname; *p; p = CharNext (p))
-                   if (*p == '\\') *p = '/';
-
-                 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
-                 _putenv (strdup (buf));
-               }
-           }
-       }
     }
 
     for (i = 0; i < N_ENV_VARS; i++)
@@ -2180,7 +2344,7 @@ init_environment (char ** argv)
                          strcpy (&fname[pend - pstart + 1], "cmdproxy.exe");
                          ExpandEnvironmentStrings ((LPSTR) fname, bufc,
                                                    sizeof (bufc));
-                         if (check_existing (bufc))
+                         if (sys_access (bufc, F_OK) == 0)
                            {
                              lpval = bufc;
                              dwType = REG_SZ;
@@ -2260,8 +2424,22 @@ init_environment (char ** argv)
   /* Remember the initial working directory for getcwd.  */
   /* FIXME: Do we need to resolve possible symlinks in startup_dir?
      Does it matter anywhere in Emacs?  */
-  if (!GetCurrentDirectory (MAXPATHLEN, startup_dir))
-    emacs_abort ();
+  if (w32_unicode_filenames)
+    {
+      wchar_t wstartup_dir[MAX_PATH];
+
+      if (!GetCurrentDirectoryW (MAX_PATH, wstartup_dir))
+       emacs_abort ();
+      filename_from_utf16 (wstartup_dir, startup_dir);
+    }
+  else
+    {
+      char astartup_dir[MAX_PATH];
+
+      if (!GetCurrentDirectoryA (MAX_PATH, astartup_dir))
+       emacs_abort ();
+      filename_from_ansi (astartup_dir, startup_dir);
+    }
 
   {
     static char modname[MAX_PATH];
@@ -2284,175 +2462,23 @@ init_environment (char ** argv)
 char *
 emacs_root_dir (void)
 {
-  static char root_dir[FILENAME_MAX];
+  static char root_dir[MAX_UTF8_PATH];
   const char *p;
 
   p = getenv ("emacs_dir");
   if (p == NULL)
     emacs_abort ();
-  strcpy (root_dir, p);
+  filename_from_ansi (p, root_dir);
   root_dir[parse_root (root_dir, NULL)] = '\0';
-  dostounix_filename (root_dir, 0);
+  dostounix_filename (root_dir);
   return root_dir;
 }
 
-/* We don't have scripts to automatically determine the system configuration
-   for Emacs before it's compiled, and we don't want to have to make the
-   user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
-   routine.  */
-
-char *
-get_emacs_configuration (void)
-{
-  char *arch, *oem, *os;
-  int build_num;
-  static char configuration_buffer[32];
-
-  /* Determine the processor type.  */
-  switch (get_processor_type ())
-    {
-
-#ifdef PROCESSOR_INTEL_386
-    case PROCESSOR_INTEL_386:
-    case PROCESSOR_INTEL_486:
-    case PROCESSOR_INTEL_PENTIUM:
-#ifdef _WIN64
-      arch = "amd64";
-#else
-      arch = "i386";
-#endif
-      break;
-#endif
-#ifdef PROCESSOR_AMD_X8664
-    case PROCESSOR_AMD_X8664:
-      arch = "amd64";
-      break;
-#endif
-
-#ifdef PROCESSOR_MIPS_R2000
-    case PROCESSOR_MIPS_R2000:
-    case PROCESSOR_MIPS_R3000:
-    case PROCESSOR_MIPS_R4000:
-      arch = "mips";
-      break;
-#endif
-
-#ifdef PROCESSOR_ALPHA_21064
-    case PROCESSOR_ALPHA_21064:
-      arch = "alpha";
-      break;
-#endif
-
-    default:
-      arch = "unknown";
-      break;
-    }
-
-  /* Use the OEM field to reflect the compiler/library combination.  */
-#ifdef _MSC_VER
-#define COMPILER_NAME  "msvc"
-#else
-#ifdef __GNUC__
-#define COMPILER_NAME  "mingw"
-#else
-#define COMPILER_NAME  "unknown"
-#endif
-#endif
-  oem = COMPILER_NAME;
-
-  switch (osinfo_cache.dwPlatformId) {
-  case VER_PLATFORM_WIN32_NT:
-    os = "nt";
-    build_num = osinfo_cache.dwBuildNumber;
-    break;
-  case VER_PLATFORM_WIN32_WINDOWS:
-    if (osinfo_cache.dwMinorVersion == 0) {
-      os = "windows95";
-    } else {
-      os = "windows98";
-    }
-    build_num = LOWORD (osinfo_cache.dwBuildNumber);
-    break;
-  case VER_PLATFORM_WIN32s:
-    /* Not supported, should not happen. */
-    os = "windows32s";
-    build_num = LOWORD (osinfo_cache.dwBuildNumber);
-    break;
-  default:
-    os = "unknown";
-    build_num = 0;
-    break;
-  }
-
-  if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
-    sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
-            get_w32_major_version (), get_w32_minor_version (), build_num);
-  } else {
-    sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
-  }
-
-  return configuration_buffer;
-}
-
-char *
-get_emacs_configuration_options (void)
-{
-  static char *options_buffer;
-  char cv[32];  /* Enough for COMPILER_VERSION.  */
-  char *options[] = {
-    cv,  /* To be filled later.  */
-#ifdef EMACSDEBUG
-    " --no-opt",
-#endif
-#ifdef ENABLE_CHECKING
-    " --enable-checking",
-#endif
-    /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS
-       with a starting space to save work here.  */
-#ifdef USER_CFLAGS
-    " --cflags", USER_CFLAGS,
-#endif
-#ifdef USER_LDFLAGS
-    " --ldflags", USER_LDFLAGS,
-#endif
-    NULL
-  };
-  size_t size = 0;
-  int i;
-
-/* Work out the effective configure options for this build.  */
-#ifdef _MSC_VER
-#define COMPILER_VERSION       "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100
-#else
-#ifdef __GNUC__
-#define COMPILER_VERSION       "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
-#else
-#define COMPILER_VERSION       ""
-#endif
-#endif
-
-  if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0)
-    return "Error: not enough space for compiler version";
-  cv[sizeof (cv) - 1] = '\0';
-
-  for (i = 0; options[i]; i++)
-    size += strlen (options[i]);
-
-  options_buffer = xmalloc (size + 1);
-  options_buffer[0] = '\0';
-
-  for (i = 0; options[i]; i++)
-    strcat (options_buffer, options[i]);
-
-  return options_buffer;
-}
-
-
 #include <sys/timeb.h>
 
 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95).  */
 int
-gettimeofday (struct timeval *restrict tv, struct timezone *restrict tz)
+gettimeofday (struct timeval *__restrict tv, struct timezone *__restrict tz)
 {
   struct _timeb tb;
   _ftime (&tb);
@@ -2487,8 +2513,6 @@ gettimeofday (struct timeval *restrict tv, struct timezone *restrict tz)
 int
 fdutimens (int fd, char const *file, struct timespec const timespec[2])
 {
-  struct _utimbuf ut;
-
   if (!timespec)
     {
       errno = ENOSYS;
@@ -2499,12 +2523,28 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2])
       errno = EBADF;
       return -1;
     }
-  ut.actime = timespec[0].tv_sec;
-  ut.modtime = timespec[1].tv_sec;
+  /* _futime's prototype defines 2nd arg as having the type 'struct
+     _utimbuf', while utime needs to accept 'struct utimbuf' for
+     compatibility with Posix.  So we need to use 2 different (but
+     equivalent) types to avoid compiler warnings, sigh.  */
   if (fd >= 0)
-    return _futime (fd, &ut);
+    {
+      struct _utimbuf _ut;
+
+      _ut.actime = timespec[0].tv_sec;
+      _ut.modtime = timespec[1].tv_sec;
+      return _futime (fd, &_ut);
+    }
   else
-    return _utime (file, &ut);
+    {
+      struct utimbuf ut;
+
+      ut.actime = timespec[0].tv_sec;
+      ut.modtime = timespec[1].tv_sec;
+      /* Call 'utime', which is implemented below, not the MS library
+        function, which fails on directories.  */
+      return utime (file, &ut);
+    }
 }
 
 
@@ -2588,6 +2628,7 @@ static void
 add_volume_info (char * root_dir, volume_info_data * info)
 {
   info->root_dir = xstrdup (root_dir);
+  unixtodos_filename (info->root_dir);
   info->next = volume_cache;
   volume_cache = info;
 }
@@ -2600,14 +2641,30 @@ static volume_info_data *
 GetCachedVolumeInformation (char * root_dir)
 {
   volume_info_data * info;
-  char default_root[ MAX_PATH ];
+  char default_root[ MAX_UTF8_PATH ];
+  char  name[MAX_PATH+1];
+  char  type[MAX_PATH+1];
 
   /* NULL for root_dir means use root from current directory.  */
   if (root_dir == NULL)
     {
-      if (GetCurrentDirectory (MAX_PATH, default_root) == 0)
-       return NULL;
-      parse_root (default_root, &root_dir);
+      if (w32_unicode_filenames)
+       {
+         wchar_t curdirw[MAX_PATH];
+
+         if (GetCurrentDirectoryW (MAX_PATH, curdirw) == 0)
+           return NULL;
+         filename_from_utf16 (curdirw, default_root);
+       }
+      else
+       {
+         char curdira[MAX_PATH];
+
+         if (GetCurrentDirectoryA (MAX_PATH, curdira) == 0)
+           return NULL;
+         filename_from_ansi (curdira, default_root);
+       }
+      parse_root (default_root, (const char **)&root_dir);
       *root_dir = 0;
       root_dir = default_root;
     }
@@ -2646,20 +2703,47 @@ GetCachedVolumeInformation (char * root_dir)
 
   if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info))
     {
-      char  name[ 256 ];
       DWORD serialnum;
       DWORD maxcomp;
       DWORD flags;
-      char  type[ 256 ];
 
       /* Info is not cached, or is stale. */
-      if (!GetVolumeInformation (root_dir,
-                                name, sizeof (name),
-                                &serialnum,
-                                &maxcomp,
-                                &flags,
-                                type, sizeof (type)))
-       return NULL;
+      if (w32_unicode_filenames)
+       {
+         wchar_t root_w[MAX_PATH];
+         wchar_t  name_w[MAX_PATH+1];
+         wchar_t  type_w[MAX_PATH+1];
+
+         filename_to_utf16 (root_dir, root_w);
+         if (!GetVolumeInformationW (root_w,
+                                    name_w, sizeof (name_w),
+                                    &serialnum,
+                                    &maxcomp,
+                                    &flags,
+                                    type_w, sizeof (type_w)))
+           return NULL;
+         /* Hmm... not really 100% correct, as these 2 are not file
+            names...  */
+         filename_from_utf16 (name_w, name);
+         filename_from_utf16 (type_w, type);
+       }
+      else
+       {
+         char root_a[MAX_PATH];
+         char  name_a[MAX_PATH+1];
+         char  type_a[MAX_PATH+1];
+
+         filename_to_ansi (root_dir, root_a);
+         if (!GetVolumeInformationA (root_a,
+                                    name_a, sizeof (name_a),
+                                    &serialnum,
+                                    &maxcomp,
+                                    &flags,
+                                    type_a, sizeof (type_a)))
+           return NULL;
+         filename_from_ansi (name_a, name);
+         filename_from_ansi (type_a, type);
+       }
 
       /* Cache the volume information for future use, overwriting existing
         entry if present.  */
@@ -2675,6 +2759,7 @@ GetCachedVolumeInformation (char * root_dir)
        }
 
       info->name = xstrdup (name);
+      unixtodos_filename (info->name);
       info->serialnum = serialnum;
       info->maxcomp = maxcomp;
       info->flags = flags;
@@ -2697,53 +2782,23 @@ GetCachedVolumeInformation (char * root_dir)
 static int
 get_volume_info (const char * name, const char ** pPath)
 {
-  char temp[MAX_PATH];
+  char temp[MAX_UTF8_PATH];
   char *rootname = NULL;  /* default to current volume */
   volume_info_data * info;
+  int root_len = parse_root (name, pPath);
 
   if (name == NULL)
     return FALSE;
 
-  /* Find the root name of the volume if given.  */
-  if (isalpha (name[0]) && name[1] == ':')
-    {
-      rootname = temp;
-      temp[0] = *name++;
-      temp[1] = *name++;
-      temp[2] = '\\';
-      temp[3] = 0;
-    }
-  else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
+  /* Copy the root name of the volume, if given.  */
+  if (root_len)
     {
-      char *str = temp;
-      int slashes = 4;
-      int dbcs_p = max_filename_mbslen () > 1;
-
+      strncpy (temp, name, root_len);
+      temp[root_len] = '\0';
+      unixtodos_filename (temp);
       rootname = temp;
-      do
-        {
-         if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
-           break;
-         if (!dbcs_p)
-           *str++ = *name++;
-         else
-           {
-             const char *p = name;
-
-             name = CharNextExA (file_name_codepage, name, 0);
-             memcpy (str, p, name - p);
-             str += name - p;
-           }
-       }
-      while ( *name );
-
-      *str++ = '\\';
-      *str = 0;
     }
 
-  if (pPath)
-    *pPath = name;
-
   info = GetCachedVolumeInformation (rootname);
   if (info != NULL)
     {
@@ -2765,18 +2820,19 @@ is_fat_volume (const char * name, const char ** pPath)
   return FALSE;
 }
 
-/* Map filename to a valid 8.3 name if necessary.
-   The result is a pointer to a static buffer, so CAVEAT EMPTOR!  */
+/* Convert all slashes in a filename to backslashes, and map filename
+   to a valid 8.3 name if necessary.  The result is a pointer to a
+   static buffer, so CAVEAT EMPTOR!  */
 const char *
 map_w32_filename (const char * name, const char ** pPath)
 {
-  static char shortname[MAX_PATH];
+  static char shortname[MAX_UTF8_PATH];
   char * str = shortname;
   char c;
   char * path;
   const char * save_name = name;
 
-  if (strlen (name) >= MAX_PATH)
+  if (strlen (name) >= sizeof (shortname))
     {
       /* Return a filename which will cause callers to fail.  */
       strcpy (shortname, "?");
@@ -2843,7 +2899,7 @@ map_w32_filename (const char * name, const char ** pPath)
                str[-1] = c;            /* replace last character of part */
              /* FALLTHRU */
            default:
-             if ( left )
+             if ( left && 'A' <= c && c <= 'Z' )
                {
                  *str++ = tolower (c); /* map to lower case (looks nicer) */
                  left--;
@@ -2878,25 +2934,28 @@ is_exec (const char * name)
         xstrcasecmp (p, ".cmd") == 0));
 }
 
-/* Emulate the Unix directory procedures opendir, closedir,
-   and readdir.  We can't use the procedures supplied in sysdep.c,
-   so we provide them here.  */
+/* Emulate the Unix directory procedures opendir, closedir, and
+   readdir.  We rename them to sys_* names because some versions of
+   MinGW startup code call opendir and readdir to glob wildcards, and
+   the code that calls them doesn't grok UTF-8 encoded file names we
+   produce in dirent->d_name[].  */
 
 struct dirent dir_static;       /* simulated directory contents */
 static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
 static int    dir_is_fat;
-static char   dir_pathname[MAXPATHLEN+1];
-static WIN32_FIND_DATA dir_find_data;
+static char   dir_pathname[MAX_UTF8_PATH];
+static WIN32_FIND_DATAW dir_find_data_w;
+static WIN32_FIND_DATAA dir_find_data_a;
 
 /* Support shares on a network resource as subdirectories of a read-only
    root directory. */
 static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
 static HANDLE open_unc_volume (const char *);
-static char  *read_unc_volume (HANDLE, char *, int);
+static void  *read_unc_volume (HANDLE, wchar_t *, char *, int);
 static void   close_unc_volume (HANDLE);
 
 DIR *
-opendir (const char *filename)
+sys_opendir (const char *filename)
 {
   DIR *dirp;
 
@@ -2925,8 +2984,8 @@ opendir (const char *filename)
   dirp->dd_loc = 0;
   dirp->dd_size = 0;
 
-  strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
-  dir_pathname[MAXPATHLEN] = '\0';
+  strncpy (dir_pathname, map_w32_filename (filename, NULL), MAX_UTF8_PATH - 1);
+  dir_pathname[MAX_UTF8_PATH - 1] = '\0';
   /* Note: We don't support symlinks to file names on FAT volumes.
      Doing so would mean punishing 99.99% of use cases by resolving
      all the possible symlinks in FILENAME, recursively.  */
@@ -2936,7 +2995,7 @@ opendir (const char *filename)
 }
 
 void
-closedir (DIR *dirp)
+sys_closedir (DIR *dirp)
 {
   /* If we have a find-handle open, close it.  */
   if (dir_find_handle != INVALID_HANDLE_VALUE)
@@ -2953,52 +3012,59 @@ closedir (DIR *dirp)
 }
 
 struct dirent *
-readdir (DIR *dirp)
+sys_readdir (DIR *dirp)
 {
   int downcase = !NILP (Vw32_downcase_file_names);
 
   if (wnet_enum_handle != INVALID_HANDLE_VALUE)
     {
       if (!read_unc_volume (wnet_enum_handle,
-                            dir_find_data.cFileName,
+                            dir_find_data_w.cFileName,
+                            dir_find_data_a.cFileName,
                             MAX_PATH))
        return NULL;
     }
   /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
   else if (dir_find_handle == INVALID_HANDLE_VALUE)
     {
-      char filename[MAXNAMLEN + 3];
+      char filename[MAX_UTF8_PATH + 2];
       int ln;
-      int dbcs_p = max_filename_mbslen () > 1;
 
       strcpy (filename, dir_pathname);
       ln = strlen (filename) - 1;
-      if (!dbcs_p)
+      if (!IS_DIRECTORY_SEP (filename[ln]))
+       strcat (filename, "\\");
+      strcat (filename, "*");
+
+      /* Note: No need to resolve symlinks in FILENAME, because
+        FindFirst opens the directory that is the target of a
+        symlink.  */
+      if (w32_unicode_filenames)
        {
-         if (!IS_DIRECTORY_SEP (filename[ln]))
-           strcat (filename, "\\");
+         wchar_t fnw[MAX_PATH];
+
+         filename_to_utf16 (filename, fnw);
+         dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w);
        }
       else
        {
-         char *end = filename + ln + 1;
-         char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
+         char fna[MAX_PATH];
 
-         if (!IS_DIRECTORY_SEP (*last_char))
-           strcat (filename, "\\");
+         filename_to_ansi (filename, fna);
+         dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
        }
-      strcat (filename, "*");
-
-      /* Note: No need to resolve symlinks in FILENAME, because
-        FindFirst opens the directory that is the target of a
-        symlink.  */
-      dir_find_handle = FindFirstFile (filename, &dir_find_data);
 
       if (dir_find_handle == INVALID_HANDLE_VALUE)
        return NULL;
     }
+  else if (w32_unicode_filenames)
+    {
+      if (!FindNextFileW (dir_find_handle, &dir_find_data_w))
+       return NULL;
+    }
   else
     {
-      if (!FindNextFile (dir_find_handle, &dir_find_data))
+      if (!FindNextFileA (dir_find_handle, &dir_find_data_a))
        return NULL;
     }
 
@@ -3006,112 +3072,153 @@ readdir (DIR *dirp)
      value returned by stat().  */
   dir_static.d_ino = 1;
 
-  strcpy (dir_static.d_name, dir_find_data.cFileName);
-
-  /* If the file name in cFileName[] includes `?' characters, it means
-     the original file name used characters that cannot be represented
-     by the current ANSI codepage.  To avoid total lossage, retrieve
-     the short 8+3 alias of the long file name.  */
-  if (_mbspbrk (dir_static.d_name, "?"))
+  if (w32_unicode_filenames)
     {
-      strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
-      downcase = 1;    /* 8+3 aliases are returned in all caps */
-    }
-  dir_static.d_namlen = strlen (dir_static.d_name);
-  dir_static.d_reclen = sizeof (struct dirent) - MAXNAMLEN + 3 +
-    dir_static.d_namlen - dir_static.d_namlen % 4;
+      if (downcase || dir_is_fat)
+       {
+         wchar_t tem[MAX_PATH];
 
-  /* If the file name in cFileName[] includes `?' characters, it means
-     the original file name used characters that cannot be represented
-     by the current ANSI codepage.  To avoid total lossage, retrieve
-     the short 8+3 alias of the long file name.  */
-  if (_mbspbrk (dir_find_data.cFileName, "?"))
-    {
-      strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
-      /* 8+3 aliases are returned in all caps, which could break
-        various alists that look at filenames' extensions.  */
-      downcase = 1;
+         wcscpy (tem, dir_find_data_w.cFileName);
+         CharLowerW (tem);
+         filename_from_utf16 (tem, dir_static.d_name);
+       }
+      else
+       filename_from_utf16 (dir_find_data_w.cFileName, dir_static.d_name);
     }
   else
-    strcpy (dir_static.d_name, dir_find_data.cFileName);
-  dir_static.d_namlen = strlen (dir_static.d_name);
-  if (dir_is_fat)
-    _mbslwr (dir_static.d_name);
-  else if (downcase)
     {
-      register char *p;
-      int dbcs_p = max_filename_mbslen () > 1;
-      for (p = dir_static.d_name; *p; )
+      char tem[MAX_PATH];
+
+      /* If the file name in cFileName[] includes `?' characters, it
+        means the original file name used characters that cannot be
+        represented by the current ANSI codepage.  To avoid total
+        lossage, retrieve the short 8+3 alias of the long file
+        name.  */
+      if (_mbspbrk (dir_find_data_a.cFileName, "?"))
        {
-         if (*p >= 'a' && *p <= 'z')
-           break;
-         if (dbcs_p)
-           p = CharNextExA (file_name_codepage, p, 0);
-         else
-           p++;
+         strcpy (tem, dir_find_data_a.cAlternateFileName);
+         /* 8+3 aliases are returned in all caps, which could break
+            various alists that look at filenames' extensions.  */
+         downcase = 1;
+       }
+      else if (downcase || dir_is_fat)
+       strcpy (tem, dir_find_data_a.cFileName);
+      else
+       filename_from_ansi (dir_find_data_a.cFileName, dir_static.d_name);
+      if (downcase || dir_is_fat)
+       {
+         _mbslwr (tem);
+         filename_from_ansi (tem, dir_static.d_name);
        }
-      if (!*p)
-       _mbslwr (dir_static.d_name);
     }
 
+  dir_static.d_namlen = strlen (dir_static.d_name);
+  dir_static.d_reclen = sizeof (struct dirent) - MAX_UTF8_PATH + 3 +
+    dir_static.d_namlen - dir_static.d_namlen % 4;
+
   return &dir_static;
 }
 
 static HANDLE
 open_unc_volume (const char *path)
 {
-  NETRESOURCE nr;
+  const char *fn = map_w32_filename (path, NULL);
+  DWORD result;
   HANDLE henum;
-  int result;
 
-  nr.dwScope = RESOURCE_GLOBALNET;
-  nr.dwType = RESOURCETYPE_DISK;
-  nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
-  nr.dwUsage = RESOURCEUSAGE_CONTAINER;
-  nr.lpLocalName = NULL;
-  nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
-  nr.lpComment = NULL;
-  nr.lpProvider = NULL;
+  if (w32_unicode_filenames)
+    {
+      NETRESOURCEW nrw;
+      wchar_t fnw[MAX_PATH];
+
+      nrw.dwScope = RESOURCE_GLOBALNET;
+      nrw.dwType = RESOURCETYPE_DISK;
+      nrw.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+      nrw.dwUsage = RESOURCEUSAGE_CONTAINER;
+      nrw.lpLocalName = NULL;
+      filename_to_utf16 (fn, fnw);
+      nrw.lpRemoteName = fnw;
+      nrw.lpComment = NULL;
+      nrw.lpProvider = NULL;
 
-  result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
-                        RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+      result = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+                             RESOURCEUSAGE_CONNECTABLE, &nrw, &henum);
+    }
+  else
+    {
+      NETRESOURCEA nra;
+      char fna[MAX_PATH];
+
+      nra.dwScope = RESOURCE_GLOBALNET;
+      nra.dwType = RESOURCETYPE_DISK;
+      nra.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+      nra.dwUsage = RESOURCEUSAGE_CONTAINER;
+      nra.lpLocalName = NULL;
+      filename_to_ansi (fn, fna);
+      nra.lpRemoteName = fna;
+      nra.lpComment = NULL;
+      nra.lpProvider = NULL;
 
+      result = WNetOpenEnumA (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+                             RESOURCEUSAGE_CONNECTABLE, &nra, &henum);
+    }
   if (result == NO_ERROR)
     return henum;
   else
     return INVALID_HANDLE_VALUE;
 }
 
-static char *
-read_unc_volume (HANDLE henum, char *readbuf, int size)
+static void *
+read_unc_volume (HANDLE henum, wchar_t *fname_w, char *fname_a, int size)
 {
   DWORD count;
   int result;
-  DWORD bufsize = 512;
   char *buffer;
-  char *ptr;
-  int dbcs_p = max_filename_mbslen () > 1;
+  DWORD bufsize = 512;
+  void *retval;
 
   count = 1;
-  buffer = alloca (bufsize);
-  result = WNetEnumResource (henum, &count, buffer, &bufsize);
-  if (result != NO_ERROR)
-    return NULL;
+  if (w32_unicode_filenames)
+    {
+      wchar_t *ptrw;
 
-  /* WNetEnumResource returns \\resource\share...skip forward to "share". */
-  ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
-  ptr += 2;
-  if (!dbcs_p)
-    while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+      bufsize *= 2;
+      buffer = alloca (bufsize);
+      result = WNetEnumResourceW (henum, &count, buffer, &bufsize);
+      if (result != NO_ERROR)
+       return NULL;
+      /* WNetEnumResource returns \\resource\share...skip forward to "share". */
+      ptrw = ((LPNETRESOURCEW) buffer)->lpRemoteName;
+      ptrw += 2;
+      while (*ptrw && *ptrw != L'/' && *ptrw != L'\\') ptrw++;
+      ptrw++;
+      wcsncpy (fname_w, ptrw, size);
+      retval = fname_w;
+    }
   else
     {
-      while (*ptr && !IS_DIRECTORY_SEP (*ptr))
-       ptr = CharNextExA (file_name_codepage, ptr, 0);
+      int dbcs_p = max_filename_mbslen () > 1;
+      char *ptra;
+
+      buffer = alloca (bufsize);
+      result = WNetEnumResourceA (henum, &count, buffer, &bufsize);
+      if (result != NO_ERROR)
+       return NULL;
+      ptra = ((LPNETRESOURCEA) buffer)->lpRemoteName;
+      ptra += 2;
+      if (!dbcs_p)
+       while (*ptra && !IS_DIRECTORY_SEP (*ptra)) ptra++;
+      else
+       {
+         while (*ptra && !IS_DIRECTORY_SEP (*ptra))
+           ptra = CharNextExA (file_name_codepage, ptra, 0);
+       }
+      ptra++;
+      strncpy (fname_a, ptra, size);
+      retval = fname_a;
     }
-  ptr++;
 
-  strncpy (readbuf, ptr, size);
-  return readbuf;
+  return retval;
 }
 
 static void
@@ -3142,13 +3249,12 @@ unc_volume_file_attributes (const char *path)
 static void
 logon_network_drive (const char *path)
 {
-  NETRESOURCE resource;
-  char share[MAX_PATH];
+  char share[MAX_UTF8_PATH];
   int n_slashes;
   char drive[4];
   UINT drvtype;
   char *p;
-  int dbcs_p;
+  DWORD val;
 
   if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
     drvtype = DRIVE_REMOTE;
@@ -3168,28 +3274,70 @@ logon_network_drive (const char *path)
     return;
 
   n_slashes = 2;
-  strncpy (share, path, MAX_PATH);
+  strncpy (share, path, MAX_UTF8_PATH);
   /* Truncate to just server and share name.  */
-  dbcs_p = max_filename_mbslen () > 1;
-  for (p = share + 2; *p && p < share + MAX_PATH; )
+  for (p = share + 2; *p && p < share + MAX_UTF8_PATH; p++)
     {
       if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
         {
           *p = '\0';
           break;
         }
-      if (dbcs_p)
-       p = CharNextExA (file_name_codepage, p, 0);
-      else
-       p++;
     }
 
-  resource.dwType = RESOURCETYPE_DISK;
-  resource.lpLocalName = NULL;
-  resource.lpRemoteName = share;
-  resource.lpProvider = NULL;
+  if (w32_unicode_filenames)
+    {
+      NETRESOURCEW resourcew;
+      wchar_t share_w[MAX_PATH];
+
+      resourcew.dwScope = RESOURCE_GLOBALNET;
+      resourcew.dwType = RESOURCETYPE_DISK;
+      resourcew.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+      resourcew.dwUsage = RESOURCEUSAGE_CONTAINER;
+      resourcew.lpLocalName = NULL;
+      filename_to_utf16 (share, share_w);
+      resourcew.lpRemoteName = share_w;
+      resourcew.lpProvider = NULL;
+
+      val = WNetAddConnection2W (&resourcew, NULL, NULL, CONNECT_INTERACTIVE);
+    }
+  else
+    {
+      NETRESOURCEA resourcea;
+      char share_a[MAX_PATH];
+
+      resourcea.dwScope = RESOURCE_GLOBALNET;
+      resourcea.dwType = RESOURCETYPE_DISK;
+      resourcea.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+      resourcea.dwUsage = RESOURCEUSAGE_CONTAINER;
+      resourcea.lpLocalName = NULL;
+      filename_to_ansi (share, share_a);
+      resourcea.lpRemoteName = share_a;
+      resourcea.lpProvider = NULL;
 
-  WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
+      val = WNetAddConnection2A (&resourcea, NULL, NULL, CONNECT_INTERACTIVE);
+    }
+
+  switch (val)
+    {
+    case NO_ERROR:
+    case ERROR_ALREADY_ASSIGNED:
+      break;
+    case ERROR_ACCESS_DENIED:
+    case ERROR_LOGON_FAILURE:
+      errno = EACCES;
+      break;
+    case ERROR_BUSY:
+      errno = EAGAIN;
+      break;
+    case ERROR_BAD_NET_NAME:
+    case ERROR_NO_NET_OR_BAD_PATH:
+    case ERROR_NO_NETWORK:
+    case ERROR_CANCELLED:
+    default:
+      errno = ENOENT;
+      break;
+    }
 }
 
 /* Emulate faccessat(2).  */
@@ -3217,7 +3365,22 @@ faccessat (int dirfd, const char * path, int mode, int flags)
       && (flags & AT_SYMLINK_NOFOLLOW) == 0)
     path = chase_symlinks (path);
 
-  if ((attributes = GetFileAttributes (path)) == -1)
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      attributes = GetFileAttributesW (path_w);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      attributes = GetFileAttributesA (path_a);
+    }
+
+  if (attributes == -1)
     {
       DWORD w32err = GetLastError ();
 
@@ -3265,6 +3428,62 @@ faccessat (int dirfd, const char * path, int mode, int flags)
   return 0;
 }
 
+/* A version of 'access' to be used locally with file names in
+   locale-specific encoding.  Does not resolve symlinks and does not
+   support file names on FAT12 and FAT16 volumes, but that's OK, since
+   we only invoke this function for files inside the Emacs source or
+   installation tree, on directories (so any symlinks should have the
+   directory bit set), and on short file names such as "C:/.emacs".  */
+static int
+sys_access (const char *fname, int mode)
+{
+  char fname_copy[MAX_PATH], *p;
+  DWORD attributes;
+
+  strcpy (fname_copy, fname);
+  /* Do the equivalent of unixtodos_filename.  */
+  for (p = fname_copy; *p; p = CharNext (p))
+    if (*p == '/')
+      *p = '\\';
+
+  if ((attributes = GetFileAttributesA (fname_copy)) == -1)
+    {
+      DWORD w32err = GetLastError ();
+
+      switch (w32err)
+       {
+       case ERROR_INVALID_NAME:
+       case ERROR_BAD_PATHNAME:
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_BAD_NETPATH:
+         errno = ENOENT;
+         break;
+       default:
+         errno = EACCES;
+         break;
+       }
+      return -1;
+    }
+  if ((mode & X_OK) != 0
+      && !(is_exec (fname_copy)
+          || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0))
+    {
+      errno = EACCES;
+      return -1;
+    }
+  if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
+    {
+      errno = EACCES;
+      return -1;
+    }
+  if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+    {
+      errno = EACCES;
+      return -1;
+    }
+  return 0;
+}
+
 /* Shadow some MSVC runtime functions to map requests for long filenames
    to reasonable short names if necessary.  This was originally added to
    permit running Emacs on NT 3.1 on a FAT partition, which doesn't support
@@ -3273,20 +3492,63 @@ faccessat (int dirfd, const char * path, int mode, int flags)
 int
 sys_chdir (const char * path)
 {
-  return _chdir (map_w32_filename (path, NULL));
+  path = map_w32_filename (path, NULL);
+  if (w32_unicode_filenames)
+    {
+      wchar_t newdir_w[MAX_PATH];
+
+      if (filename_to_utf16 (path, newdir_w) == 0)
+       return _wchdir (newdir_w);
+      return -1;
+    }
+  else
+    {
+      char newdir_a[MAX_PATH];
+
+      if (filename_to_ansi (path, newdir_a) == 0)
+       return _chdir (newdir_a);
+      return -1;
+    }
 }
 
 int
 sys_chmod (const char * path, int mode)
 {
   path = chase_symlinks (map_w32_filename (path, NULL));
-  return _chmod (path, mode);
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      return _wchmod (path_w, mode);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      return _chmod (path_a, mode);
+    }
 }
 
 int
 sys_creat (const char * path, int mode)
 {
-  return _creat (map_w32_filename (path, NULL), mode);
+  path = map_w32_filename (path, NULL);
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      return _wcreat (path_w, mode);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      return _creat (path_a, mode);
+    }
 }
 
 FILE *
@@ -3326,7 +3588,21 @@ sys_fopen (const char * path, const char * mode)
       }
     else break;
 
-  fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
+  path = map_w32_filename (path, NULL);
+  if (w32_unicode_filenames)
+    {
+      wchar_t path_w[MAX_PATH];
+
+      filename_to_utf16 (path, path_w);
+      fd = _wopen (path_w, oflag | _O_NOINHERIT, 0644);
+    }
+  else
+    {
+      char path_a[MAX_PATH];
+
+      filename_to_ansi (path, path_a);
+      fd = _open (path_a, oflag | _O_NOINHERIT, 0644);
+    }
   if (fd < 0)
     return NULL;
 
@@ -3339,7 +3615,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)
     {
@@ -3350,8 +3628,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;
@@ -3368,7 +3656,10 @@ 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,
+      /* 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 = MultiByteToWideChar (CP_UTF8, 0, newname, -1,
                                  data.wid.cStreamName, MAX_PATH);
       if (wlen > 0)
        {
@@ -3392,9 +3683,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;
+               }
            }
        }
 
@@ -3412,25 +3734,46 @@ sys_mkdir (const char * path)
   return _mkdir (map_w32_filename (path, NULL));
 }
 
-/* Because of long name mapping issues, we need to implement this
-   ourselves.  Also, MSVC's _mktemp returns NULL when it can't generate
-   a unique name, instead of setting the input template to an empty
-   string.
+int
+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);
+
+  return res;
+}
+
+/* Implementation of mkostemp for MS-Windows, to avoid race conditions
+   when using mktemp.
 
-   Standard algorithm seems to be use pid or tid with a letter on the
-   front (in place of the 6 X's) and cycle through the letters to find a
-   unique name.  We extend that to allow any reasonable character as the
-   first of the 6 X's.  */
-char *
-sys_mktemp (char * template)
+   Standard algorithm for generating a temporary file name seems to be
+   use pid or tid with a letter on the front (in place of the 6 X's)
+   and cycle through the letters to find a unique name.  We extend
+   that to allow any reasonable character as the first of the 6 X's,
+   so that the number of simultaneously used temporary files will be
+   greater.  */
+
+int
+mkostemp (char * template, int flags)
 {
   char * p;
-  int i;
+  int i, fd = -1;
   unsigned uid = GetCurrentThreadId ();
+  int save_errno = errno;
   static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#";
 
+  errno = EINVAL;
   if (template == NULL)
-    return NULL;
+    return -1;
+
   p = template + strlen (template);
   i = 5;
   /* replace up to the last 5 X's with uid in decimal */
@@ -3445,38 +3788,22 @@ sys_mktemp (char * template)
       i = 0;
       do
        {
-         int save_errno = errno;
          p[0] = first_char[i];
-         if (faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0)
+         if ((fd = sys_open (template,
+                             flags | _O_CREAT | _O_EXCL | _O_RDWR,
+                             S_IRUSR | S_IWUSR)) >= 0
+             || errno != EEXIST)
            {
-             errno = save_errno;
-             return template;
+             if (fd >= 0)
+               errno = save_errno;
+             return fd;
            }
        }
       while (++i < sizeof (first_char));
     }
 
-  /* Template is badly formed or else we can't generate a unique name,
-     so return empty string */
-  template[0] = 0;
-  return template;
-}
-
-int
-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);
-
-  return res;
+  /* Template is badly formed or else we can't generate a unique name.  */
+  return -1;
 }
 
 int
@@ -3686,49 +4013,6 @@ convert_from_time_t (time_t time, FILETIME * pft)
   pft->dwLowDateTime = tmp.LowPart;
 }
 
-#if 0
-/* No reason to keep this; faking inode values either by hashing or even
-   using the file index from GetInformationByHandle, is not perfect and
-   so by default Emacs doesn't use the inode values on Windows.
-   Instead, we now determine file-truename correctly (except for
-   possible drive aliasing etc).  */
-
-/*  Modified version of "PJW" algorithm (see the "Dragon" compiler book). */
-static unsigned
-hashval (const unsigned char * str)
-{
-  unsigned h = 0;
-  while (*str)
-    {
-      h = (h << 4) + *str++;
-      h ^= (h >> 28);
-    }
-  return h;
-}
-
-/* Return the hash value of the canonical pathname, excluding the
-   drive/UNC header, to get a hopefully unique inode number. */
-static DWORD
-generate_inode_val (const char * name)
-{
-  char fullname[ MAX_PATH ];
-  char * p;
-  unsigned hash;
-
-  /* Get the truly canonical filename, if it exists.  (Note: this
-     doesn't resolve aliasing due to subst commands, or recognize hard
-     links.  */
-  if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH))
-    emacs_abort ();
-
-  parse_root (fullname, &p);
-  /* Normal W32 filesystems are still case insensitive. */
-  _strlwr (p);
-  return hashval (p);
-}
-
-#endif
-
 static PSECURITY_DESCRIPTOR
 get_file_security_desc_by_handle (HANDLE h)
 {
@@ -3785,12 +4069,6 @@ get_rid (PSID sid)
 
 /* Caching SID and account values for faster lokup.  */
 
-#ifdef __GNUC__
-# define FLEXIBLE_ARRAY_MEMBER
-#else
-# define FLEXIBLE_ARRAY_MEMBER 1
-#endif
-
 struct w32_id {
   unsigned rid;
   struct w32_id *next;
@@ -4252,7 +4530,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks)
              && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
            {
              /* This was the last entry returned by readdir.  */
-             wfd = dir_find_data;
+             wfd = dir_find_data_a; /* FIXME!!! */
            }
          else
            {
@@ -4486,6 +4764,9 @@ fstat (int desc, struct stat * buf)
   return 0;
 }
 
+/* A version of 'utime' which handles directories as well as
+   files.  */
+
 int
 utime (const char *name, struct utimbuf *times)
 {
@@ -4500,13 +4781,32 @@ utime (const char *name, struct utimbuf *times)
       times = &deftime;
     }
 
-  /* Need write access to set times.  */
-  fh = CreateFile (name, FILE_WRITE_ATTRIBUTES,
-                  /* If NAME specifies a directory, FILE_SHARE_DELETE
-                     allows other processes to delete files inside it,
-                     while we have the directory open.  */
-                  FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
-                  0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+  if (w32_unicode_filenames)
+    {
+      wchar_t name_utf16[MAX_PATH];
+
+      if (filename_to_utf16 (name, name_utf16) != 0)
+       return -1;      /* errno set by filename_to_utf16 */
+
+      /* Need write access to set times.  */
+      fh = CreateFileW (name_utf16, FILE_WRITE_ATTRIBUTES,
+                       /* If NAME specifies a directory, FILE_SHARE_DELETE
+                          allows other processes to delete files inside it,
+                          while we have the directory open.  */
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
+  else
+    {
+      char name_ansi[MAX_PATH];
+
+      if (filename_to_ansi (name, name_ansi) != 0)
+       return -1;      /* errno set by filename_to_ansi */
+
+      fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES,
+                       FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+    }
   if (fh != INVALID_HANDLE_VALUE)
     {
       convert_from_time_t (times->actime, &atime);
@@ -4521,7 +4821,31 @@ utime (const char *name, struct utimbuf *times)
     }
   else
     {
-      errno = EINVAL;
+      DWORD err = GetLastError ();
+
+      switch (err)
+       {
+       case ERROR_FILE_NOT_FOUND:
+       case ERROR_PATH_NOT_FOUND:
+       case ERROR_INVALID_DRIVE:
+       case ERROR_BAD_NETPATH:
+       case ERROR_DEV_NOT_EXIST:
+         /* ERROR_INVALID_NAME is the error CreateFile sets when the
+            file name includes ?s, i.e. translation to ANSI failed.  */
+       case ERROR_INVALID_NAME:
+         errno = ENOENT;
+         break;
+       case ERROR_TOO_MANY_OPEN_FILES:
+         errno = ENFILE;
+         break;
+       case ERROR_ACCESS_DENIED:
+       case ERROR_SHARING_VIOLATION:
+         errno = EACCES;
+         break;
+       default:
+         errno = EINVAL;
+         break;
+       }
       return -1;
     }
   return 0;
@@ -5754,8 +6078,8 @@ system_process_attributes (Lisp_Object pid)
                {
                  /* Decode the command name from locale-specific
                     encoding.  */
-                 cmd_str = make_unibyte_string (pe.szExeFile,
-                                                strlen (pe.szExeFile));
+                 cmd_str = build_unibyte_string (pe.szExeFile);
+
                  decoded_cmd =
                    code_convert_string_norecord (cmd_str,
                                                  Vlocale_coding_system, 0);
@@ -6060,6 +6384,7 @@ term_winsock (void)
 {
   if (winsock_lib != NULL && winsock_inuse == 0)
     {
+      release_listen_threads ();
       /* Not sure what would cause WSAENETDOWN, or even if it can happen
         after WSAStartup returns successfully, but it seems reasonable
         to allow unloading winsock anyway in that case. */
@@ -6725,10 +7050,16 @@ sys_sendto (int s, const char * buf, int len, int flags,
 }
 
 /* Windows does not have an fcntl function.  Provide an implementation
-   solely for making sockets non-blocking.  */
+   good enough for Emacs.  */
 int
 fcntl (int s, int cmd, int options)
 {
+  /* In the w32 Emacs port, fcntl (fd, F_DUPFD_CLOEXEC, fd1) is always
+     invoked in a context where fd1 is closed and all descriptors less
+     than fd1 are open, so sys_dup is an adequate implementation.  */
+  if (cmd == F_DUPFD_CLOEXEC)
+    return sys_dup (s);
+
   if (winsock_lib == NULL)
     {
       errno = ENETDOWN;
@@ -6870,13 +7201,14 @@ sys_dup2 (int src, int dst)
   return rc;
 }
 
-/* Unix pipe() has only one arg */
 int
-sys_pipe (int * phandles)
+pipe2 (int * phandles, int pipe2_flags)
 {
   int rc;
   unsigned flags;
 
+  eassert (pipe2_flags == O_CLOEXEC);
+
   /* make pipe handles non-inheritable; when we spawn a child, we
      replace the relevant handle with an inheritable one.  Also put
      pipes into binary mode; we will do text mode translation ourselves
@@ -7037,7 +7369,12 @@ _sys_wait_accept (int fd)
   rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
   if (rc != SOCKET_ERROR)
     {
-      rc = WaitForSingleObject (hEv, INFINITE);
+      do {
+       rc = WaitForSingleObject (hEv, 500);
+       Sleep (5);
+      } while (rc == WAIT_TIMEOUT
+              && cp->status != STATUS_READ_ERROR
+              && cp->char_avail);
       pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
       if (rc == WAIT_OBJECT_0)
        cp->status = STATUS_READ_SUCCEEDED;
@@ -7387,6 +7724,269 @@ sys_write (int fd, const void * buffer, unsigned int count)
   return nchars;
 }
 
+\f
+/* Emulation of SIOCGIFCONF and getifaddrs, see process.c.  */
+
+extern Lisp_Object conv_sockaddr_to_lisp (struct sockaddr *, int);
+
+/* Return information about network interface IFNAME, or about all
+   interfaces (if IFNAME is nil).  */
+static Lisp_Object
+network_interface_get_info (Lisp_Object ifname)
+{
+  ULONG ainfo_len = sizeof (IP_ADAPTER_INFO);
+  IP_ADAPTER_INFO *adapter, *ainfo = xmalloc (ainfo_len);
+  DWORD retval = get_adapters_info (ainfo, &ainfo_len);
+  Lisp_Object res = Qnil;
+
+  if (retval == ERROR_BUFFER_OVERFLOW)
+    {
+      ainfo = xrealloc (ainfo, ainfo_len);
+      retval = get_adapters_info (ainfo, &ainfo_len);
+    }
+
+  if (retval == ERROR_SUCCESS)
+    {
+      int eth_count = 0, tr_count = 0, fddi_count = 0, ppp_count = 0;
+      int sl_count = 0, wlan_count = 0, lo_count = 0, ifx_count = 0;
+      int if_num;
+      struct sockaddr_in sa;
+
+      /* For the below, we need some winsock functions, so make sure
+        the winsock DLL is loaded.  If we cannot successfully load
+        it, they will have no use of the information we provide,
+        anyway, so punt.  */
+      if (!winsock_lib && !init_winsock (1))
+       goto done;
+
+      for (adapter = ainfo; adapter; adapter = adapter->Next)
+       {
+         char namebuf[MAX_ADAPTER_NAME_LENGTH + 4];
+         u_long ip_addr;
+         /* Present Unix-compatible interface names, instead of the
+            Windows names, which are really GUIDs not readable by
+            humans.  */
+         static const char *ifmt[] = {
+           "eth%d", "tr%d", "fddi%d", "ppp%d", "sl%d", "wlan%d",
+           "lo", "ifx%d"
+         };
+         enum {
+           NONE = -1,
+           ETHERNET = 0,
+           TOKENRING = 1,
+           FDDI = 2,
+           PPP = 3,
+           SLIP = 4,
+           WLAN = 5,
+           LOOPBACK = 6,
+           OTHER_IF = 7
+         } ifmt_idx;
+
+         switch (adapter->Type)
+           {
+           case MIB_IF_TYPE_ETHERNET:
+             /* Windows before Vista reports wireless adapters as
+                Ethernet.  Work around by looking at the Description
+                string.  */
+             if (strstr (adapter->Description, "Wireless "))
+               {
+                 ifmt_idx = WLAN;
+                 if_num = wlan_count++;
+               }
+             else
+               {
+                 ifmt_idx = ETHERNET;
+                 if_num = eth_count++;
+               }
+             break;
+           case MIB_IF_TYPE_TOKENRING:
+             ifmt_idx = TOKENRING;
+             if_num = tr_count++;
+             break;
+           case MIB_IF_TYPE_FDDI:
+             ifmt_idx = FDDI;
+             if_num = fddi_count++;
+             break;
+           case MIB_IF_TYPE_PPP:
+             ifmt_idx = PPP;
+             if_num = ppp_count++;
+             break;
+           case MIB_IF_TYPE_SLIP:
+             ifmt_idx = SLIP;
+             if_num = sl_count++;
+             break;
+           case IF_TYPE_IEEE80211:
+             ifmt_idx = WLAN;
+             if_num = wlan_count++;
+             break;
+           case MIB_IF_TYPE_LOOPBACK:
+             if (lo_count < 0)
+               {
+                 ifmt_idx = LOOPBACK;
+                 if_num = lo_count++;
+               }
+             else
+               ifmt_idx = NONE;
+             break;
+           default:
+             ifmt_idx = OTHER_IF;
+             if_num = ifx_count++;
+             break;
+           }
+         if (ifmt_idx == NONE)
+           continue;
+         sprintf (namebuf, ifmt[ifmt_idx], if_num);
+
+         sa.sin_family = AF_INET;
+         ip_addr = sys_inet_addr (adapter->IpAddressList.IpAddress.String);
+         if (ip_addr == INADDR_NONE)
+           {
+             /* Bogus address, skip this interface.  */
+             continue;
+           }
+         sa.sin_addr.s_addr = ip_addr;
+         sa.sin_port = 0;
+         if (NILP (ifname))
+           res = Fcons (Fcons (build_string (namebuf),
+                               conv_sockaddr_to_lisp ((struct sockaddr*) &sa,
+                                                      sizeof (struct sockaddr))),
+                        res);
+         else if (strcmp (namebuf, SSDATA (ifname)) == 0)
+           {
+             Lisp_Object hwaddr = Fmake_vector (make_number (6), Qnil);
+             register struct Lisp_Vector *p = XVECTOR (hwaddr);
+             Lisp_Object flags = Qnil;
+             int n;
+             u_long net_mask;
+
+             /* Flags.  We guess most of them by type, since the
+                Windows flags are different and hard to get by.  */
+             flags = Fcons (intern ("up"), flags);
+             if (ifmt_idx == ETHERNET || ifmt_idx == WLAN)
+               {
+                 flags = Fcons (intern ("broadcast"), flags);
+                 flags = Fcons (intern ("multicast"), flags);
+               }
+             flags = Fcons (intern ("running"), flags);
+             if (ifmt_idx == PPP)
+               {
+                 flags = Fcons (intern ("pointopoint"), flags);
+                 flags = Fcons (intern ("noarp"), flags);
+               }
+             if (adapter->HaveWins)
+               flags = Fcons (intern ("WINS"), flags);
+             if (adapter->DhcpEnabled)
+               flags = Fcons (intern ("dynamic"), flags);
+
+             res = Fcons (flags, res);
+
+             /* Hardware address and its family.  */
+             for (n = 0; n < adapter->AddressLength; n++)
+               p->u.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.  */
+             res = Fcons (Fcons (make_number (1), hwaddr), res);
+
+             /* Network mask.  */
+             sa.sin_family = AF_INET;
+             net_mask = sys_inet_addr (adapter->IpAddressList.IpMask.String);
+             if (net_mask != INADDR_NONE)
+               {
+                 sa.sin_addr.s_addr = net_mask;
+                 sa.sin_port = 0;
+                 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                     sizeof (struct sockaddr)),
+                              res);
+               }
+             else
+               res = Fcons (Qnil, res);
+
+             sa.sin_family = AF_INET;
+             if (ip_addr != INADDR_NONE)
+               {
+                 /* Broadcast address is only reported by
+                    GetAdaptersAddresses, which is of limited
+                    availability.  Generate it on our own.  */
+                 u_long bcast_addr = (ip_addr & net_mask) | ~net_mask;
+
+                 sa.sin_addr.s_addr = bcast_addr;
+                 sa.sin_port = 0;
+                 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                     sizeof (struct sockaddr)),
+                              res);
+
+                 /* IP address.  */
+                 sa.sin_addr.s_addr = ip_addr;
+                 sa.sin_port = 0;
+                 res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                     sizeof (struct sockaddr)),
+                              res);
+               }
+             else
+               res = Fcons (Qnil, Fcons (Qnil, res));
+           }
+       }
+      /* GetAdaptersInfo is documented to not report loopback
+        interfaces, so we generate one out of thin air.  */
+      if (!lo_count)
+       {
+         sa.sin_family = AF_INET;
+         sa.sin_port = 0;
+         if (NILP (ifname))
+           {
+             sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1");
+             res = Fcons (Fcons (build_string ("lo"),
+                                 conv_sockaddr_to_lisp ((struct sockaddr*) &sa,
+                                                        sizeof (struct sockaddr))),
+                          res);
+           }
+         else if (strcmp (SSDATA (ifname), "lo") == 0)
+           {
+             res = Fcons (Fcons (intern ("running"),
+                                 Fcons (intern ("loopback"),
+                                        Fcons (intern ("up"), Qnil))), Qnil);
+             /* 772 is what 3 different GNU/Linux systems report for
+                the loopback interface.  */
+             res = Fcons (Fcons (make_number (772),
+                                 Fmake_vector (make_number (6),
+                                               make_number (0))),
+                          res);
+             sa.sin_addr.s_addr = sys_inet_addr ("255.0.0.0");
+             res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                 sizeof (struct sockaddr)),
+                          res);
+             sa.sin_addr.s_addr = sys_inet_addr ("0.0.0.0");
+             res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                 sizeof (struct sockaddr)),
+                          res);
+             sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1");
+             res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa,
+                                                 sizeof (struct sockaddr)),
+                          res);
+           }
+
+       }
+    }
+
+ done:
+  xfree (ainfo);
+  return res;
+}
+
+Lisp_Object
+network_interface_list (void)
+{
+  return network_interface_get_info (Qnil);
+}
+
+Lisp_Object
+network_interface_info (Lisp_Object ifname)
+{
+  return network_interface_get_info (ifname);
+}
+
+\f
 /* The Windows CRT functions are "optimized for speed", so they don't
    check for timezone and DST changes if they were last called less
    than 1 minute ago (see http://support.microsoft.com/kb/821231).  So
@@ -7688,6 +8288,7 @@ globals_of_w32 (void)
   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_get_adapters_info = 0;
   num_of_processors = 0;
   /* The following sets a handler for shutdown notifications for
      console apps. This actually applies to Emacs in both console and
@@ -7700,12 +8301,21 @@ globals_of_w32 (void)
 
   /* Reset, in case it has some value inherited from dump time.  */
   w32_stat_get_owner_group = 0;
+
+  /* If w32_unicode_filenames is non-zero, we will be using Unicode
+     (a.k.a. "wide") APIs to invoke functions that accept file
+     names.  */
+  if (is_windows_9x ())
+    w32_unicode_filenames = 0;
+  else
+    w32_unicode_filenames = 1;
 }
 
 /* For make-serial-process  */
 int
-serial_open (char *port)
+serial_open (Lisp_Object port_obj)
 {
+  char *port = SSDATA (port_obj);
   HANDLE hnd;
   child_process *cp;
   int fd = -1;
@@ -7899,7 +8509,7 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
 {
   int n, err;
   SELECT_TYPE fdset;
-  EMACS_TIME timeout;
+  struct timespec timeout;
   struct Lisp_Process *process = (struct Lisp_Process *)p;
   int fd = process->infd;