]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Converted map_w32_filename and its subroutines.
[gnu-emacs] / src / w32.c
index 431826c4b82ad93b557f51bb54d869d8caa7c18a..f0812cc342080a7e2af9dfbcb5b3cb69849a3db6 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
@@ -65,6 +66,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #undef localtime
 
 #include "lisp.h"
+#include "epaths.h"    /* for SHELL */
 
 #include <pwd.h>
 #include <grp.h>
@@ -89,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>
 
@@ -201,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"
@@ -229,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
@@ -277,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
@@ -419,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
@@ -1111,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
@@ -1135,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 *
@@ -1327,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 =
 {
@@ -1509,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)
@@ -1537,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;
-
-      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 */
+  CPINFO cp_info;
 
-      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')
     {
@@ -1631,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.
@@ -1725,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;
@@ -1743,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]))
@@ -1770,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;
 }
 
@@ -1796,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.  */
@@ -1809,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';
@@ -1818,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)
@@ -1842,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)
 {
@@ -1850,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;
@@ -1950,8 +2137,6 @@ w32_get_resource (char *key, LPDWORD lpdwtype)
   return (NULL);
 }
 
-char *get_emacs_configuration (void);
-
 void
 init_environment (char ** argv)
 {
@@ -1963,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
@@ -1978,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);
@@ -2018,7 +2210,7 @@ init_environment (char ** argv)
       {"PRELOAD_WINSOCK", NULL},
       {"emacs_dir", "C:/emacs"},
       {"EMACSLOADPATH", NULL},
-      {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
+      {"SHELL", "cmdproxy.exe"}, /* perhaps it is somewhere on PATH */
       {"EMACSDATA", NULL},
       {"EMACSPATH", NULL},
       {"INFOPATH", NULL},
@@ -2041,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
@@ -2094,9 +2286,12 @@ init_environment (char ** argv)
        emacs_abort ();
       *p = 0;
 
-      if ((p = _mbsrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
+      if ((p = _mbsrchr (modname, '\\'))
+         /* From bin means installed Emacs, from src means uninstalled.  */
+         && (xstrcasecmp (p, "\\bin") == 0 || xstrcasecmp (p, "\\src") == 0))
        {
          char buf[SET_ENV_BUF_SIZE];
+         int within_build_tree = xstrcasecmp (p, "\\src") == 0;
 
          *p = 0;
          for (p = modname; *p; p = CharNext (p))
@@ -2104,32 +2299,14 @@ init_environment (char ** argv)
 
          _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
          _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)
+         /* If we are running from the Posix-like build tree, define
+            SHELL to point to our own cmdproxy.  The loop below will
+            then disregard PATH_EXEC and the default value.  */
+         if (within_build_tree)
            {
-             *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));
-               }
+             _snprintf (buf, sizeof (buf) - 1,
+                        "SHELL=%s/nt/cmdproxy.exe", modname);
+             _putenv (strdup (buf));
            }
        }
     }
@@ -2139,16 +2316,60 @@ init_environment (char ** argv)
        if (!getenv (env_vars[i].name))
          {
            int dont_free = 0;
+           char bufc[SET_ENV_BUF_SIZE];
 
            if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
                /* Also ignore empty environment variables.  */
                || *lpval == 0)
              {
                xfree (lpval);
-               lpval = env_vars[i].def_value;
-               dwType = REG_EXPAND_SZ;
                dont_free = 1;
-               if (!strcmp (env_vars[i].name, "HOME") && !appdata)
+               if (strcmp (env_vars[i].name, "SHELL") == 0)
+                 {
+                   /* Look for cmdproxy.exe in every directory in
+                      PATH_EXEC.  FIXME: This does not find cmdproxy
+                      in nt/ when we run uninstalled.  */
+                   char fname[MAX_PATH];
+                   const char *pstart = PATH_EXEC, *pend;
+
+                   do {
+                     pend = _mbschr (pstart, ';');
+                     if (!pend)
+                       pend = pstart + strlen (pstart);
+                     /* Be defensive against series of ;;; characters.  */
+                     if (pend > pstart)
+                       {
+                         strncpy (fname, pstart, pend - pstart);
+                         fname[pend - pstart] = '/';
+                         strcpy (&fname[pend - pstart + 1], "cmdproxy.exe");
+                         ExpandEnvironmentStrings ((LPSTR) fname, bufc,
+                                                   sizeof (bufc));
+                         if (sys_access (bufc, F_OK) == 0)
+                           {
+                             lpval = bufc;
+                             dwType = REG_SZ;
+                             break;
+                           }
+                       }
+                     if (*pend)
+                       pstart = pend + 1;
+                     else
+                       pstart = pend;
+                     if (!*pstart)
+                       {
+                         /* If not found in any directory, use the
+                            default as the last resort.  */
+                         lpval = env_vars[i].def_value;
+                         dwType = REG_EXPAND_SZ;
+                       }
+                   } while (*pstart);
+                 }
+               else
+                 {
+                   lpval = env_vars[i].def_value;
+                   dwType = REG_EXPAND_SZ;
+                 }
+               if (strcmp (env_vars[i].name, "HOME") == 0 && !appdata)
                  Vdelayed_warnings_list
                    = Fcons (listn (CONSTYPE_HEAP, 2,
                                    intern ("initialization"),
@@ -2203,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];
@@ -2227,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).  */
-void
-gettimeofday (struct timeval *tv, struct timezone *tz)
+int
+gettimeofday (struct timeval *__restrict tv, struct timezone *__restrict tz)
 {
   struct _timeb tb;
   _ftime (&tb);
@@ -2413,6 +2496,7 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
       tz->tz_minuteswest = tb.timezone;        /* minutes west of Greenwich  */
       tz->tz_dsttime = tb.dstflag;     /* type of dst correction  */
     }
+  return 0;
 }
 
 /* Emulate fdutimens.  */
@@ -2429,8 +2513,6 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
 int
 fdutimens (int fd, char const *file, struct timespec const timespec[2])
 {
-  struct _utimbuf ut;
-
   if (!timespec)
     {
       errno = ENOSYS;
@@ -2441,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;
-  if (fd >= 0)
-    return _futime (fd, &ut);
+  /* _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)
+    {
+      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);
+    }
 }
 
 
@@ -2530,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;
 }
@@ -2542,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;
     }
@@ -2588,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.  */
@@ -2617,6 +2759,7 @@ GetCachedVolumeInformation (char * root_dir)
        }
 
       info->name = xstrdup (name);
+      unixtodos_filename (info->name);
       info->serialnum = serialnum;
       info->maxcomp = maxcomp;
       info->flags = flags;
@@ -2639,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)
     {
@@ -2707,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, "?");
@@ -2785,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--;
@@ -3207,6 +3321,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
@@ -3215,7 +3385,25 @@ faccessat (int dirfd, const char * path, int mode, int flags)
 int
 sys_chdir (const char * path)
 {
-  return _chdir (map_w32_filename (path, NULL));
+  /* FIXME: Temporary.  Also, figure out what to do with
+     map_w32_filename, as the original code did this:
+     _chdir(map_w32_filename (path, NULL)).  */
+  if (w32_unicode_filenames)
+    {
+      wchar_t newdir[MAXPATHLEN];
+
+      if (filename_to_utf16 (path, newdir) == 0)
+       return _wchdir (newdir);
+      return -1;
+    }
+  else
+    {
+      char newdir[MAXPATHLEN];
+
+      if (filename_to_ansi (path, newdir) == 0)
+       return _chdir (path);
+      return -1;
+    }
 }
 
 int
@@ -3354,25 +3542,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 */
@@ -3387,38 +3596,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
@@ -3628,49 +3821,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)
 {
@@ -3727,12 +3877,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;
@@ -4428,6 +4572,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)
 {
@@ -4442,13 +4589,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);
@@ -4463,7 +4629,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;
@@ -5696,8 +5886,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);
@@ -6002,6 +6192,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. */
@@ -6667,10 +6858,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;
@@ -6812,13 +7009,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
@@ -6979,7 +7177,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;
@@ -7329,6 +7532,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
@@ -7630,6 +8096,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
@@ -7642,12 +8109,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;
@@ -7841,7 +8317,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;