* coding.c (make_conversion_work_buffer): Disable buffer modification
[bpt/emacs.git] / src / w32.c
index d9d11c3..c6660c0 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,13 +1,13 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
-   Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004,
-                 2005, 2006, 2007 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+                 2007, 2008  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -15,10 +15,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING.  If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA.
+along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
+/*
    Geoff Voelker (voelker@cs.washington.edu)                         7-29-94
 */
 #include <stddef.h> /* for offsetof */
@@ -33,6 +32,7 @@ Boston, MA 02110-1301, USA.
 #include <sys/time.h>
 #include <sys/utime.h>
 #include <mbstring.h>  /* for _mbspbrk */
+#include <math.h>
 
 /* must include CRT headers *before* config.h */
 
@@ -73,8 +73,41 @@ Boston, MA 02110-1301, USA.
 #define _ANONYMOUS_STRUCT
 #endif
 #include <windows.h>
+/* Some versions of compiler define MEMORYSTATUSEX, some don't, so we
+   use a different name to avoid compilation problems.  */
+typedef struct _MEMORY_STATUS_EX {
+        DWORD dwLength;
+        DWORD dwMemoryLoad;
+        DWORDLONG ullTotalPhys;
+        DWORDLONG ullAvailPhys;
+        DWORDLONG ullTotalPageFile;
+        DWORDLONG ullAvailPageFile;
+        DWORDLONG ullTotalVirtual;
+        DWORDLONG ullAvailVirtual;
+        DWORDLONG ullAvailExtendedVirtual;
+} MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX;
+
+#include <lmcons.h>
 #include <shlobj.h>
 
+#include <tlhelp32.h>
+#include <psapi.h>
+/* This either is not in psapi.h or guarded by higher value of
+   _WIN32_WINNT than what we use.  */
+typedef struct _PROCESS_MEMORY_COUNTERS_EX {
+       DWORD cb;
+       DWORD PageFaultCount;
+       DWORD PeakWorkingSetSize;
+       DWORD WorkingSetSize;
+       DWORD QuotaPeakPagedPoolUsage;
+       DWORD QuotaPagedPoolUsage;
+       DWORD QuotaPeakNonPagedPoolUsage;
+       DWORD QuotaNonPagedPoolUsage;
+       DWORD PagefileUsage;
+       DWORD PeakPagefileUsage;
+       DWORD PrivateUsage;
+} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
+
 #ifdef HAVE_SOCKETS    /* TCP connection support, if kernel can do it */
 #include <sys/socket.h>
 #undef socket
@@ -100,26 +133,64 @@ Boston, MA 02110-1301, USA.
 #include "ndir.h"
 #include "w32heap.h"
 #include "systime.h"
+#include "dispextern.h"                /* for xstrcasecmp */
+#include "coding.h"            /* for Vlocale_coding_system */
+
+/* For serial_configure and serial_open.  */
+#include "process.h"
+/* From process.c  */
+extern Lisp_Object QCport, QCspeed, QCprocess;
+extern Lisp_Object QCbytesize, QCstopbits, QCparity, Qodd, Qeven;
+extern Lisp_Object QCflowcontrol, Qhw, Qsw, QCsummary;
 
 typedef HRESULT (WINAPI * ShGetFolderPath_fn)
   (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
 
 void globals_of_w32 ();
+static DWORD get_rid (PSID);
 
 extern Lisp_Object Vw32_downcase_file_names;
 extern Lisp_Object Vw32_generate_fake_inodes;
 extern Lisp_Object Vw32_get_true_file_attributes;
+/* Defined in process.c for its own purpose.  */
+extern Lisp_Object Qlocal;
+
 extern int w32_num_mouse_buttons;
 
 \f
-/*
-  Initialization states
- */
+/* Initialization states.
+
+   WARNING: If you add any more such variables for additional APIs,
+            you MUST add initialization for them to globals_of_w32
+            below.  This is because these variables might get set
+            to non-NULL values during dumping, but the dumped Emacs
+            cannot reuse those values, because it could be run on a
+            different version of the OS, where API addresses are
+            different.  */
 static BOOL g_b_init_is_windows_9x;
 static BOOL g_b_init_open_process_token;
 static BOOL g_b_init_get_token_information;
 static BOOL g_b_init_lookup_account_sid;
 static BOOL g_b_init_get_sid_identifier_authority;
+static BOOL g_b_init_get_sid_sub_authority;
+static BOOL g_b_init_get_sid_sub_authority_count;
+static BOOL g_b_init_get_file_security;
+static BOOL g_b_init_get_security_descriptor_owner;
+static BOOL g_b_init_get_security_descriptor_group;
+static BOOL g_b_init_is_valid_sid;
+static BOOL g_b_init_create_toolhelp32_snapshot;
+static BOOL g_b_init_process32_first;
+static BOOL g_b_init_process32_next;
+static BOOL g_b_init_open_thread_token;
+static BOOL g_b_init_impersonate_self;
+static BOOL g_b_init_revert_to_self;
+static BOOL g_b_init_get_process_memory_info;
+static BOOL g_b_init_get_process_working_set_size;
+static BOOL g_b_init_global_memory_status;
+static BOOL g_b_init_global_memory_status_ex;
+static BOOL g_b_init_get_length_sid;
+static BOOL g_b_init_equal_sid;
+static BOOL g_b_init_copy_sid;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -148,8 +219,10 @@ GetProcessTimes_Proc get_process_times_fn = NULL;
 
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
+const char * const GetFileSecurity_Name =  "GetFileSecurityW";
 #else
 const char * const LookupAccountSid_Name = "LookupAccountSidA";
+const char * const GetFileSecurity_Name =  "GetFileSecurityA";
 #endif
 typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     LPCTSTR lpSystemName,
@@ -161,6 +234,67 @@ typedef BOOL (WINAPI * LookupAccountSid_Proc) (
     PSID_NAME_USE peUse);
 typedef PSID_IDENTIFIER_AUTHORITY (WINAPI * GetSidIdentifierAuthority_Proc) (
     PSID pSid);
+typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
+    PSID pSid,
+    DWORD n);
+typedef PUCHAR (WINAPI * GetSidSubAuthorityCount_Proc) (
+    PSID pSid);
+typedef BOOL (WINAPI * GetFileSecurity_Proc) (
+    LPCTSTR lpFileName,
+    SECURITY_INFORMATION RequestedInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    DWORD nLength,
+    LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pOwner,
+    LPBOOL lpbOwnerDefaulted);
+typedef BOOL (WINAPI * GetSecurityDescriptorGroup_Proc) (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pGroup,
+    LPBOOL lpbGroupDefaulted);
+typedef BOOL (WINAPI * IsValidSid_Proc) (
+    PSID sid);
+typedef HANDLE (WINAPI * CreateToolhelp32Snapshot_Proc) (
+    DWORD dwFlags,
+    DWORD th32ProcessID);
+typedef BOOL (WINAPI * Process32First_Proc) (
+    HANDLE hSnapshot,
+    LPPROCESSENTRY32 lppe);
+typedef BOOL (WINAPI * Process32Next_Proc) (
+    HANDLE hSnapshot,
+    LPPROCESSENTRY32 lppe);
+typedef BOOL (WINAPI * OpenThreadToken_Proc) (
+    HANDLE ThreadHandle,
+    DWORD DesiredAccess,
+    BOOL OpenAsSelf,
+    PHANDLE TokenHandle);
+typedef BOOL (WINAPI * ImpersonateSelf_Proc) (
+    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel);
+typedef BOOL (WINAPI * RevertToSelf_Proc) (void);
+typedef BOOL (WINAPI * GetProcessMemoryInfo_Proc) (
+    HANDLE Process,
+    PPROCESS_MEMORY_COUNTERS ppsmemCounters,
+    DWORD cb);
+typedef BOOL (WINAPI * GetProcessWorkingSetSize_Proc) (
+    HANDLE hProcess,
+    DWORD * lpMinimumWorkingSetSize,
+    DWORD * lpMaximumWorkingSetSize);
+typedef BOOL (WINAPI * GlobalMemoryStatus_Proc) (
+    LPMEMORYSTATUS lpBuffer);
+typedef BOOL (WINAPI * GlobalMemoryStatusEx_Proc) (
+    LPMEMORY_STATUS_EX lpBuffer);
+typedef BOOL (WINAPI * CopySid_Proc) (
+    DWORD nDestinationSidLength,
+    PSID pDestinationSid,
+    PSID pSourceSid);
+typedef BOOL (WINAPI * EqualSid_Proc) (
+    PSID pSid1,
+    PSID pSid2);
+typedef DWORD (WINAPI * GetLengthSid_Proc) (
+    PSID pSid);
+
+
 
   /* ** A utility function ** */
 static BOOL
@@ -349,6 +483,240 @@ PSID_IDENTIFIER_AUTHORITY WINAPI get_sid_identifier_authority (
   return (s_pfn_Get_Sid_Identifier_Authority (pSid));
 }
 
+PDWORD WINAPI get_sid_sub_authority (
+    PSID pSid,
+    DWORD n)
+{
+  static GetSidSubAuthority_Proc s_pfn_Get_Sid_Sub_Authority = NULL;
+  static DWORD zero = 0U;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return &zero;
+    }
+  if (g_b_init_get_sid_sub_authority == 0)
+    {
+      g_b_init_get_sid_sub_authority = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Sid_Sub_Authority =
+        (GetSidSubAuthority_Proc) GetProcAddress (
+            hm_advapi32, "GetSidSubAuthority");
+    }
+  if (s_pfn_Get_Sid_Sub_Authority == NULL)
+    {
+      return &zero;
+    }
+  return (s_pfn_Get_Sid_Sub_Authority (pSid, n));
+}
+
+PUCHAR WINAPI get_sid_sub_authority_count (
+    PSID pSid)
+{
+  static GetSidSubAuthorityCount_Proc s_pfn_Get_Sid_Sub_Authority_Count = NULL;
+  static UCHAR zero = 0U;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return &zero;
+    }
+  if (g_b_init_get_sid_sub_authority_count == 0)
+    {
+      g_b_init_get_sid_sub_authority_count = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Sid_Sub_Authority_Count =
+        (GetSidSubAuthorityCount_Proc) GetProcAddress (
+            hm_advapi32, "GetSidSubAuthorityCount");
+    }
+  if (s_pfn_Get_Sid_Sub_Authority_Count == NULL)
+    {
+      return &zero;
+    }
+  return (s_pfn_Get_Sid_Sub_Authority_Count (pSid));
+}
+
+BOOL WINAPI get_file_security (
+    LPCTSTR lpFileName,
+    SECURITY_INFORMATION RequestedInformation,
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    DWORD nLength,
+    LPDWORD lpnLengthNeeded)
+{
+  static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_file_security == 0)
+    {
+      g_b_init_get_file_security = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_File_Security =
+        (GetFileSecurity_Proc) GetProcAddress (
+            hm_advapi32, GetFileSecurity_Name);
+    }
+  if (s_pfn_Get_File_Security == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
+                                  pSecurityDescriptor, nLength,
+                                  lpnLengthNeeded));
+}
+
+BOOL WINAPI get_security_descriptor_owner (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pOwner,
+    LPBOOL lpbOwnerDefaulted)
+{
+  static GetSecurityDescriptorOwner_Proc s_pfn_Get_Security_Descriptor_Owner = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_security_descriptor_owner == 0)
+    {
+      g_b_init_get_security_descriptor_owner = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Descriptor_Owner =
+        (GetSecurityDescriptorOwner_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityDescriptorOwner");
+    }
+  if (s_pfn_Get_Security_Descriptor_Owner == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Descriptor_Owner (pSecurityDescriptor, pOwner,
+                                              lpbOwnerDefaulted));
+}
+
+BOOL WINAPI get_security_descriptor_group (
+    PSECURITY_DESCRIPTOR pSecurityDescriptor,
+    PSID *pGroup,
+    LPBOOL lpbGroupDefaulted)
+{
+  static GetSecurityDescriptorGroup_Proc s_pfn_Get_Security_Descriptor_Group = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_security_descriptor_group == 0)
+    {
+      g_b_init_get_security_descriptor_group = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Security_Descriptor_Group =
+        (GetSecurityDescriptorGroup_Proc) GetProcAddress (
+            hm_advapi32, "GetSecurityDescriptorGroup");
+    }
+  if (s_pfn_Get_Security_Descriptor_Group == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Get_Security_Descriptor_Group (pSecurityDescriptor, pGroup,
+                                              lpbGroupDefaulted));
+}
+
+BOOL WINAPI is_valid_sid (
+    PSID sid)
+{
+  static IsValidSid_Proc s_pfn_Is_Valid_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_is_valid_sid == 0)
+    {
+      g_b_init_is_valid_sid = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Is_Valid_Sid =
+        (IsValidSid_Proc) GetProcAddress (
+            hm_advapi32, "IsValidSid");
+    }
+  if (s_pfn_Is_Valid_Sid == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Is_Valid_Sid (sid));
+}
+
+BOOL WINAPI equal_sid (
+    PSID sid1,
+    PSID sid2)
+{
+  static EqualSid_Proc s_pfn_Equal_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_equal_sid == 0)
+    {
+      g_b_init_equal_sid = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Equal_Sid =
+        (EqualSid_Proc) GetProcAddress (
+            hm_advapi32, "EqualSid");
+    }
+  if (s_pfn_Equal_Sid == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Equal_Sid (sid1, sid2));
+}
+
+DWORD WINAPI get_length_sid (
+    PSID sid)
+{
+  static GetLengthSid_Proc s_pfn_Get_Length_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return 0;
+    }
+  if (g_b_init_get_length_sid == 0)
+    {
+      g_b_init_get_length_sid = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Get_Length_Sid =
+        (GetLengthSid_Proc) GetProcAddress (
+            hm_advapi32, "GetLengthSid");
+    }
+  if (s_pfn_Get_Length_Sid == NULL)
+    {
+      return 0;
+    }
+  return (s_pfn_Get_Length_Sid (sid));
+}
+
+BOOL WINAPI copy_sid (
+    DWORD destlen,
+    PSID dest,
+    PSID src)
+{
+  static CopySid_Proc s_pfn_Copy_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_copy_sid == 0)
+    {
+      g_b_init_copy_sid = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Copy_Sid =
+        (CopySid_Proc) GetProcAddress (
+            hm_advapi32, "CopySid");
+    }
+  if (s_pfn_Copy_Sid == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Copy_Sid (destlen, dest, src));
+}
+
 /*
   END: Wrapper functions around OpenProcessToken
   and other functions in advapi32.dll that are only
@@ -444,35 +812,38 @@ getloadavg (double loadavg[], int nelem)
 
 #define PASSWD_FIELD_SIZE 256
 
-static char the_passwd_name[PASSWD_FIELD_SIZE];
-static char the_passwd_passwd[PASSWD_FIELD_SIZE];
-static char the_passwd_gecos[PASSWD_FIELD_SIZE];
-static char the_passwd_dir[PASSWD_FIELD_SIZE];
-static char the_passwd_shell[PASSWD_FIELD_SIZE];
+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 struct passwd the_passwd =
+static struct passwd dflt_passwd =
 {
-  the_passwd_name,
-  the_passwd_passwd,
+  dflt_passwd_name,
+  dflt_passwd_passwd,
   0,
   0,
   0,
-  the_passwd_gecos,
-  the_passwd_dir,
-  the_passwd_shell,
+  dflt_passwd_gecos,
+  dflt_passwd_dir,
+  dflt_passwd_shell,
 };
 
-static struct group the_group =
+static char dflt_group_name[GNLEN+1];
+
+static struct group dflt_group =
 {
-  /* There are no groups on NT, so we just return "root" as the
-     group name.  */
-  "root",
+  /* When group information is not available, we return this as the
+     group for all files.  */
+  dflt_group_name,
+  0,
 };
 
 int
 getuid ()
 {
-  return the_passwd.pw_uid;
+  return dflt_passwd.pw_uid;
 }
 
 int
@@ -487,7 +858,7 @@ geteuid ()
 int
 getgid ()
 {
-  return the_passwd.pw_gid;
+  return dflt_passwd.pw_gid;
 }
 
 int
@@ -499,15 +870,15 @@ getegid ()
 struct passwd *
 getpwuid (int uid)
 {
-  if (uid == the_passwd.pw_uid)
-    return &the_passwd;
+  if (uid == dflt_passwd.pw_uid)
+    return &dflt_passwd;
   return NULL;
 }
 
 struct group *
 getgrgid (gid_t gid)
 {
-  return &the_group;
+  return &dflt_group;
 }
 
 struct passwd *
@@ -519,7 +890,7 @@ getpwnam (char *name)
   if (!pw)
     return pw;
 
-  if (stricmp (name, pw->pw_name))
+  if (xstrcasecmp (name, pw->pw_name))
     return NULL;
 
   return pw;
@@ -535,73 +906,96 @@ init_user_info ()
      the user-sid as the user id value (same for group id using the
      primary group sid from the process token). */
 
-  char         user_sid[256], name[256], domain[256];
-  DWORD        length = sizeof (name), dlength = sizeof (domain), trash;
+  char         uname[UNLEN+1], gname[GNLEN+1], domain[1025];
+  DWORD        ulength = sizeof (uname), dlength = sizeof (domain), needed;
+  DWORD               glength = sizeof (gname);
   HANDLE       token = NULL;
   SID_NAME_USE user_type;
-
-  if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
-      && get_token_information (token, TokenUser,
-                               (PVOID) user_sid, sizeof (user_sid), &trash)
-      && lookup_account_sid (NULL, *((PSID *) user_sid), name, &length,
-                            domain, &dlength, &user_type))
+  unsigned char *buf = NULL;
+  DWORD        blen = 0;
+  TOKEN_USER   user_token;
+  TOKEN_PRIMARY_GROUP group_token;
+  BOOL         result;
+
+  result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
+  if (result)
+    {
+      result = get_token_information (token, TokenUser, NULL, 0, &blen);
+      if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
+       {
+         buf = xmalloc (blen);
+         result = get_token_information (token, TokenUser,
+                                         (LPVOID)buf, blen, &needed);
+         if (result)
+           {
+             memcpy (&user_token, buf, sizeof (user_token));
+             result = lookup_account_sid (NULL, user_token.User.Sid,
+                                          uname, &ulength,
+                                          domain, &dlength, &user_type);
+           }
+       }
+      else
+       result = FALSE;
+    }
+  if (result)
     {
-      strcpy (the_passwd.pw_name, name);
-      /* Determine a reasonable uid value. */
-      if (stricmp ("administrator", name) == 0)
+      strcpy (dflt_passwd.pw_name, uname);
+      /* Determine a reasonable uid value.  */
+      if (xstrcasecmp ("administrator", uname) == 0)
        {
-         the_passwd.pw_uid = 0;
-         the_passwd.pw_gid = 0;
+         dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
+         dflt_passwd.pw_gid = 513; /* well-known None gid */
        }
       else
        {
-         SID_IDENTIFIER_AUTHORITY * pSIA;
-
-         pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
-         /* I believe the relative portion is the last 4 bytes (of 6)
-            with msb first. */
-         the_passwd.pw_uid = ((pSIA->Value[2] << 24) +
-                              (pSIA->Value[3] << 16) +
-                              (pSIA->Value[4] << 8)  +
-                              (pSIA->Value[5] << 0));
-         /* restrict to conventional uid range for normal users */
-         the_passwd.pw_uid = the_passwd.pw_uid % 60001;
-
-         /* Get group id */
-         if (get_token_information (token, TokenPrimaryGroup,
-                                    (PVOID) user_sid, sizeof (user_sid), &trash))
+         /* Use the last sub-authority value of the RID, the relative
+            portion of the SID, as user/group ID. */
+         dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
+
+         /* Get group id and name.  */
+         result = get_token_information (token, TokenPrimaryGroup,
+                                         (LPVOID)buf, blen, &needed);
+         if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
+           {
+             buf = xrealloc (buf, blen = needed);
+             result = get_token_information (token, TokenPrimaryGroup,
+                                             (LPVOID)buf, blen, &needed);
+           }
+         if (result)
            {
-             SID_IDENTIFIER_AUTHORITY * pSIA;
-
-             pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
-             the_passwd.pw_gid = ((pSIA->Value[2] << 24) +
-                                  (pSIA->Value[3] << 16) +
-                                  (pSIA->Value[4] << 8)  +
-                                  (pSIA->Value[5] << 0));
-             /* I don't know if this is necessary, but for safety... */
-             the_passwd.pw_gid = the_passwd.pw_gid % 60001;
+             memcpy (&group_token, buf, sizeof (group_token));
+             dflt_passwd.pw_gid = get_rid (group_token.PrimaryGroup);
+             dlength = sizeof (domain);
+             /* If we can get at the real Primary Group name, use that.
+                Otherwise, the default group name was already set to
+                "None" in globals_of_w32.  */
+             if (lookup_account_sid (NULL, group_token.PrimaryGroup,
+                                     gname, &glength, NULL, &dlength,
+                                     &user_type))
+               strcpy (dflt_group_name, gname);
            }
          else
-           the_passwd.pw_gid = the_passwd.pw_uid;
+           dflt_passwd.pw_gid = dflt_passwd.pw_uid;
        }
     }
   /* If security calls are not supported (presumably because we
-     are running under Windows 95), fallback to this. */
-  else if (GetUserName (name, &length))
+     are running under Windows 9X), fallback to this: */
+  else if (GetUserName (uname, &ulength))
     {
-      strcpy (the_passwd.pw_name, name);
-      if (stricmp ("administrator", name) == 0)
-       the_passwd.pw_uid = 0;
+      strcpy (dflt_passwd.pw_name, uname);
+      if (xstrcasecmp ("administrator", uname) == 0)
+       dflt_passwd.pw_uid = 0;
       else
-       the_passwd.pw_uid = 123;
-      the_passwd.pw_gid = the_passwd.pw_uid;
+       dflt_passwd.pw_uid = 123;
+      dflt_passwd.pw_gid = dflt_passwd.pw_uid;
     }
   else
     {
-      strcpy (the_passwd.pw_name, "unknown");
-      the_passwd.pw_uid = 123;
-      the_passwd.pw_gid = 123;
+      strcpy (dflt_passwd.pw_name, "unknown");
+      dflt_passwd.pw_uid = 123;
+      dflt_passwd.pw_gid = 123;
     }
+  dflt_group.gr_gid = dflt_passwd.pw_gid;
 
   /* Ensure HOME and SHELL are defined. */
   if (getenv ("HOME") == NULL)
@@ -610,9 +1004,10 @@ init_user_info ()
     abort ();
 
   /* Set dir and shell from environment variables. */
-  strcpy (the_passwd.pw_dir, getenv ("HOME"));
-  strcpy (the_passwd.pw_shell, getenv ("SHELL"));
+  strcpy (dflt_passwd.pw_dir, getenv ("HOME"));
+  strcpy (dflt_passwd.pw_shell, getenv ("SHELL"));
 
+  xfree (buf);
   if (token)
     CloseHandle (token);
 }
@@ -930,7 +1325,7 @@ w32_get_resource (key, lpdwtype)
          return (lpvalue);
        }
 
-      if (lpvalue) xfree (lpvalue);
+      xfree (lpvalue);
 
       RegCloseKey (hrootkey);
     }
@@ -947,7 +1342,7 @@ w32_get_resource (key, lpdwtype)
          return (lpvalue);
        }
 
-      if (lpvalue) xfree (lpvalue);
+      xfree (lpvalue);
 
       RegCloseKey (hrootkey);
     }
@@ -1048,25 +1443,20 @@ init_environment (char ** argv)
       HRESULT profile_result;
       /* Dynamically load ShGetFolderPath, as it won't exist on versions
         of Windows 95 and NT4 that have not been updated to include
-        MSIE 5.  Also we don't link with shell32.dll by default.  */
-      HMODULE shell32_dll;
+        MSIE 5.  */
       ShGetFolderPath_fn get_folder_path;
-      shell32_dll = GetModuleHandle ("shell32.dll");
       get_folder_path = (ShGetFolderPath_fn)
-       GetProcAddress (shell32_dll, "SHGetFolderPathA");
+       GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA");
 
       if (get_folder_path != NULL)
        {
          profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL,
                                            0, default_home);
 
-         /* If we can't get the appdata dir, revert to old behaviour.  */
+         /* If we can't get the appdata dir, revert to old behavior.  */
          if (profile_result == S_OK)
            env_vars[0].def_value = default_home;
        }
-
-      /* Unload shell32.dll, it is not needed anymore.  */
-      FreeLibrary (shell32_dll);
     }
 
   /* Get default locale info and use it for LANG.  */
@@ -1099,7 +1489,7 @@ init_environment (char ** argv)
        abort ();
       *p = 0;
 
-      if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
+      if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
        {
          char buf[SET_ENV_BUF_SIZE];
 
@@ -1115,7 +1505,7 @@ init_environment (char ** argv)
       /* 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 && stricmp (p, "\\i386") == 0)
+      else if (p && xstrcasecmp (p, "\\i386") == 0)
        {
          *p = 0;
          p = strrchr (modname, '\\');
@@ -1123,7 +1513,7 @@ init_environment (char ** argv)
            {
              *p = 0;
              p = strrchr (modname, '\\');
-             if (p && stricmp (p, "\\src") == 0)
+             if (p && xstrcasecmp (p, "\\src") == 0)
                {
                  char buf[SET_ENV_BUF_SIZE];
 
@@ -1148,7 +1538,7 @@ init_environment (char ** argv)
                /* Also ignore empty environment variables.  */
                || *lpval == 0)
              {
-               if (lpval) xfree (lpval);
+               xfree (lpval);
                lpval = env_vars[i].def_value;
                dwType = REG_EXPAND_SZ;
                dont_free = 1;
@@ -1269,12 +1659,6 @@ get_emacs_configuration (void)
       break;
 #endif
 
-#ifdef PROCESSOR_INTEL_860
-    case PROCESSOR_INTEL_860:
-      arch = "i860";
-      break;
-#endif
-
 #ifdef PROCESSOR_MIPS_R2000
     case PROCESSOR_MIPS_R2000:
     case PROCESSOR_MIPS_R3000:
@@ -1461,7 +1845,7 @@ lookup_volume_info (char * root_dir)
   volume_info_data * info;
 
   for (info = volume_cache; info; info = info->next)
-    if (stricmp (info->root_dir, root_dir) == 0)
+    if (xstrcasecmp (info->root_dir, root_dir) == 0)
       break;
   return info;
 }
@@ -1628,7 +2012,7 @@ is_fat_volume (const char * name, const char ** pPath)
   return FALSE;
 }
 
-/* Map filename to a legal 8.3 name if necessary. */
+/* Map filename to a valid 8.3 name if necessary. */
 const char *
 map_w32_filename (const char * name, const char ** pPath)
 {
@@ -1739,10 +2123,10 @@ is_exec (const char * name)
   char * p = strrchr (name, '.');
   return
     (p != NULL
-     && (stricmp (p, ".exe") == 0 ||
-        stricmp (p, ".com") == 0 ||
-        stricmp (p, ".bat") == 0 ||
-        stricmp (p, ".cmd") == 0));
+     && (xstrcasecmp (p, ".exe") == 0 ||
+        xstrcasecmp (p, ".com") == 0 ||
+        xstrcasecmp (p, ".bat") == 0 ||
+        xstrcasecmp (p, ".cmd") == 0));
 }
 
 /* Emulate the Unix directory procedures opendir, closedir,
@@ -1816,6 +2200,8 @@ closedir (DIR *dirp)
 struct direct *
 readdir (DIR *dirp)
 {
+  int downcase = !NILP (Vw32_downcase_file_names);
+
   if (wnet_enum_handle != INVALID_HANDLE_VALUE)
     {
       if (!read_unc_volume (wnet_enum_handle,
@@ -1850,14 +2236,38 @@ 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, "?"))
+    {
+      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 direct) - MAXNAMLEN + 3 +
     dir_static.d_namlen - dir_static.d_namlen % 4;
 
-  dir_static.d_namlen = strlen (dir_find_data.cFileName);
-  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_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;
+    }
+  else
+    strcpy (dir_static.d_name, dir_find_data.cFileName);
+  dir_static.d_namlen = strlen (dir_static.d_name);
   if (dir_is_fat)
     _strlwr (dir_static.d_name);
-  else if (!NILP (Vw32_downcase_file_names))
+  else if (downcase)
     {
       register char *p;
       for (p = dir_static.d_name; *p; p++)
@@ -1944,6 +2354,41 @@ unc_volume_file_attributes (const char *path)
   return attrs;
 }
 
+/* Ensure a network connection is authenticated.  */
+static void
+logon_network_drive (const char *path)
+{
+  NETRESOURCE resource;
+  char share[MAX_PATH];
+  int i, n_slashes;
+  char drive[4];
+
+  sprintf (drive, "%c:\\", path[0]);
+
+  /* Only logon to networked drives.  */
+  if ((!IS_DIRECTORY_SEP (path[0]) || !IS_DIRECTORY_SEP (path[1]))
+      && GetDriveType (drive) != DRIVE_REMOTE)
+    return;
+
+  n_slashes = 2;
+  strncpy (share, path, MAX_PATH);
+  /* Truncate to just server and share name.  */
+  for (i = 2; i < MAX_PATH; i++)
+    {
+      if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
+        {
+          share[i] = '\0';
+          break;
+        }
+    }
+
+  resource.dwType = RESOURCETYPE_DISK;
+  resource.lpLocalName = NULL;
+  resource.lpRemoteName = share;
+  resource.lpProvider = NULL;
+
+  WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
+}
 
 /* Shadow some MSVC runtime functions to map requests for long filenames
    to reasonable short names if necessary.  This was originally added to
@@ -2005,7 +2450,7 @@ sys_chmod (const char * path, int mode)
 int
 sys_chown (const char *path, uid_t owner, gid_t group)
 {
-  if (sys_chmod (path, _S_IREAD) == -1) /* check if file exists */
+  if (sys_chmod (path, S_IREAD) == -1) /* check if file exists */
     return -1;
   return 0;
 }
@@ -2254,7 +2699,7 @@ sys_rename (const char * oldname, const char * newname)
        return -1;
     }
 
-  /* Emulate Unix behaviour - newname is deleted if it already exists
+  /* Emulate Unix behavior - newname is deleted if it already exists
      (at least if it is a file; don't do this for directories).
 
      Since we mustn't do this if we are just changing the case of the
@@ -2297,6 +2742,14 @@ static FILETIME utc_base_ft;
 static long double utc_base;
 static int init = 0;
 
+static long double
+convert_time_raw (FILETIME ft)
+{
+  return
+    (long double) ft.dwHighDateTime
+    * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
+}
+
 static time_t
 convert_time (FILETIME ft)
 {
@@ -2324,12 +2777,10 @@ convert_time (FILETIME ft)
   if (CompareFileTime (&ft, &utc_base_ft) < 0)
     return 0;
 
-  ret = (long double) ft.dwHighDateTime
-    * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
-  ret -= utc_base;
-  return (time_t) (ret * 1e-7L);
+  return (time_t) ((convert_time_raw (ft) - utc_base) * 1e-7L);
 }
 
+
 void
 convert_from_time_t (time_t time, FILETIME * pft)
 {
@@ -2403,6 +2854,200 @@ generate_inode_val (const char * name)
 
 #endif
 
+static PSECURITY_DESCRIPTOR
+get_file_security_desc (const char *fname)
+{
+  PSECURITY_DESCRIPTOR psd = NULL;
+  DWORD sd_len, err;
+  SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
+    | GROUP_SECURITY_INFORMATION  /* | DACL_SECURITY_INFORMATION */ ;
+
+  if (!get_file_security (fname, si, psd, 0, &sd_len))
+    {
+      err = GetLastError ();
+      if (err != ERROR_INSUFFICIENT_BUFFER)
+       return NULL;
+    }
+
+  psd = xmalloc (sd_len);
+  if (!get_file_security (fname, si, psd, sd_len, &sd_len))
+    {
+      xfree (psd);
+      return NULL;
+    }
+
+  return psd;
+}
+
+static DWORD
+get_rid (PSID sid)
+{
+  unsigned n_subauthorities;
+
+  /* Use the last sub-authority value of the RID, the relative
+     portion of the SID, as user/group ID. */
+  n_subauthorities = *get_sid_sub_authority_count (sid);
+  if (n_subauthorities < 1)
+    return 0;  /* the "World" RID */
+  return *get_sid_sub_authority (sid, n_subauthorities - 1);
+}
+
+/* Caching SID and account values for faster lokup.  */
+
+#ifdef __GNUC__
+# define FLEXIBLE_ARRAY_MEMBER
+#else
+# define FLEXIBLE_ARRAY_MEMBER 1
+#endif
+
+struct w32_id {
+  int rid;
+  struct w32_id *next;
+  char name[GNLEN+1];
+  unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
+};
+
+static struct w32_id *w32_idlist;
+
+static int
+w32_cached_id (PSID sid, int *id, char *name)
+{
+  struct w32_id *tail, *found;
+
+  for (found = NULL, tail = w32_idlist; tail; tail = tail->next)
+    {
+      if (equal_sid ((PSID)tail->sid, sid))
+       {
+         found = tail;
+         break;
+       }
+    }
+  if (found)
+    {
+      *id = found->rid;
+      strcpy (name, found->name);
+      return 1;
+    }
+  else
+    return 0;
+}
+
+static void
+w32_add_to_cache (PSID sid, int id, char *name)
+{
+  DWORD sid_len;
+  struct w32_id *new_entry;
+
+  /* We don't want to leave behind stale cache from when Emacs was
+     dumped.  */
+  if (initialized)
+    {
+      sid_len = get_length_sid (sid);
+      new_entry = xmalloc (offsetof (struct w32_id, sid) + sid_len);
+      if (new_entry)
+       {
+         new_entry->rid = id;
+         strcpy (new_entry->name, name);
+         copy_sid (sid_len, (PSID)new_entry->sid, sid);
+         new_entry->next = w32_idlist;
+         w32_idlist = new_entry;
+       }
+    }
+}
+
+#define UID 1
+#define GID 2
+
+static int
+get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
+                int *id, char *nm, int what)
+{
+  PSID sid = NULL;
+  char machine[MAX_COMPUTERNAME_LENGTH+1];
+  BOOL dflt;
+  SID_NAME_USE ignore;
+  char name[UNLEN+1];
+  DWORD name_len = sizeof (name);
+  char domain[1024];
+  DWORD domain_len = sizeof(domain);
+  char *mp = NULL;
+  int use_dflt = 0;
+  int result;
+
+  if (what == UID)
+    result = get_security_descriptor_owner (psd, &sid, &dflt);
+  else if (what == GID)
+    result = get_security_descriptor_group (psd, &sid, &dflt);
+  else
+    result = 0;
+
+  if (!result || !is_valid_sid (sid))
+    use_dflt = 1;
+  else if (!w32_cached_id (sid, id, nm))
+    {
+      /* If FNAME is a UNC, we need to lookup account on the
+        specified machine.  */
+      if (IS_DIRECTORY_SEP (fname[0]) && IS_DIRECTORY_SEP (fname[1])
+         && fname[2] != '\0')
+       {
+         const char *s;
+         char *p;
+
+         for (s = fname + 2, p = machine;
+              *s && !IS_DIRECTORY_SEP (*s); s++, p++)
+           *p = *s;
+         *p = '\0';
+         mp = machine;
+       }
+
+      if (!lookup_account_sid (mp, sid, name, &name_len,
+                              domain, &domain_len, &ignore)
+         || name_len > UNLEN+1)
+       use_dflt = 1;
+      else
+       {
+         *id = get_rid (sid);
+         strcpy (nm, name);
+         w32_add_to_cache (sid, *id, name);
+       }
+    }
+  return use_dflt;
+}
+
+static void
+get_file_owner_and_group (
+    PSECURITY_DESCRIPTOR psd,
+    const char *fname,
+    struct stat *st)
+{
+  int dflt_usr = 0, dflt_grp = 0;
+
+  if (!psd)
+    {
+      dflt_usr = 1;
+      dflt_grp = 1;
+    }
+  else
+    {
+      if (get_name_and_id (psd, fname, &st->st_uid, st->st_uname, UID))
+       dflt_usr = 1;
+      if (get_name_and_id (psd, fname, &st->st_gid, st->st_gname, GID))
+       dflt_grp = 1;
+    }
+  /* Consider files to belong to current user/group, if we cannot get
+     more accurate information.  */
+  if (dflt_usr)
+    {
+      st->st_uid = dflt_passwd.pw_uid;
+      strcpy (st->st_uname, dflt_passwd.pw_name);
+    }
+  if (dflt_grp)
+    {
+      st->st_gid = dflt_passwd.pw_gid;
+      strcpy (st->st_gname, dflt_group.gr_name);
+    }
+}
+
 /* MSVC stat function can't cope with UNC names and has other bugs, so
    replace it with our own.  This also allows us to calculate consistent
    inode values without hacks in the main Emacs code. */
@@ -2410,12 +3055,15 @@ int
 stat (const char * path, struct stat * buf)
 {
   char *name, *r;
+  char drive_root[4];
+  UINT devtype;
   WIN32_FIND_DATA wfd;
   HANDLE fh;
-  DWORD fake_inode;
+  unsigned __int64 fake_inode;
   int permission;
   int len;
   int rootdir = FALSE;
+  PSECURITY_DESCRIPTOR psd = NULL;
 
   if (path == NULL || buf == NULL)
     {
@@ -2494,13 +3142,15 @@ stat (const char * path, struct stat * buf)
       if (dir_find_handle != INVALID_HANDLE_VALUE
          && strnicmp (name, dir_pathname, len) == 0
          && IS_DIRECTORY_SEP (name[len])
-         && stricmp (name + len + 1, dir_static.d_name) == 0)
+         && xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
        {
          /* This was the last entry returned by readdir.  */
          wfd = dir_find_data;
        }
       else
        {
+          logon_network_drive (name);
+
          fh = FindFirstFile (name, &wfd);
          if (fh == INVALID_HANDLE_VALUE)
            {
@@ -2511,7 +3161,19 @@ stat (const char * path, struct stat * buf)
        }
     }
 
-  if (!NILP (Vw32_get_true_file_attributes)
+  /* GetDriveType needs the root directory of NAME's drive.  */
+  if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
+    devtype = GetDriveType (NULL); /* use root of current diectory */
+  else
+    {
+      strncpy (drive_root, name, 3);
+      drive_root[3] = '\0';
+      devtype = GetDriveType (drive_root);
+    }
+
+  if (!(NILP (Vw32_get_true_file_attributes)
+       || (EQ (Vw32_get_true_file_attributes, Qlocal)
+           && devtype != DRIVE_FIXED && devtype != DRIVE_RAMDISK))
       /* No access rights required to get info.  */
       && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS, NULL))
@@ -2530,7 +3192,9 @@ stat (const char * path, struct stat * buf)
             all the time (even then there are situations where it is
             not unique).  Reputedly, there are at most 48 bits of info
             (on NTFS, presumably less on FAT). */
-         fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+         fake_inode = info.nFileIndexHigh;
+         fake_inode <<= 32;
+         fake_inode += info.nFileIndexLow;
        }
       else
        {
@@ -2540,34 +3204,39 @@ stat (const char * path, struct stat * buf)
 
       if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
-         buf->st_mode = _S_IFDIR;
+         buf->st_mode = S_IFDIR;
        }
       else
        {
          switch (GetFileType (fh))
            {
            case FILE_TYPE_DISK:
-             buf->st_mode = _S_IFREG;
+             buf->st_mode = S_IFREG;
              break;
            case FILE_TYPE_PIPE:
-             buf->st_mode = _S_IFIFO;
+             buf->st_mode = S_IFIFO;
              break;
            case FILE_TYPE_CHAR:
            case FILE_TYPE_UNKNOWN:
            default:
-             buf->st_mode = _S_IFCHR;
+             buf->st_mode = S_IFCHR;
            }
        }
       CloseHandle (fh);
+      psd = get_file_security_desc (name);
+      get_file_owner_and_group (psd, name, buf);
     }
   else
     {
       /* Don't bother to make this information more accurate.  */
       buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
-       _S_IFDIR : _S_IFREG;
+       S_IFDIR : S_IFREG;
       buf->st_nlink = 1;
       fake_inode = 0;
+
+      get_file_owner_and_group (NULL, name, buf);
     }
+  xfree (psd);
 
 #if 0
   /* Not sure if there is any point in this.  */
@@ -2587,16 +3256,14 @@ stat (const char * path, struct stat * buf)
   else
     buf->st_ino = fake_inode;
 
-  /* consider files to belong to current user */
-  buf->st_uid = the_passwd.pw_uid;
-  buf->st_gid = the_passwd.pw_gid;
-
   /* volume_info is set indirectly by map_w32_filename */
   buf->st_dev = volume_info.serialnum;
   buf->st_rdev = volume_info.serialnum;
 
 
-  buf->st_size = wfd.nFileSizeLow;
+  buf->st_size = wfd.nFileSizeHigh;
+  buf->st_size <<= 32;
+  buf->st_size += wfd.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
   buf->st_mtime = convert_time (wfd.ftLastWriteTime);
@@ -2607,14 +3274,14 @@ stat (const char * path, struct stat * buf)
 
   /* determine rwx permissions */
   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
-    permission = _S_IREAD;
+    permission = S_IREAD;
   else
-    permission = _S_IREAD | _S_IWRITE;
+    permission = S_IREAD | S_IWRITE;
 
   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-    permission |= _S_IEXEC;
+    permission |= S_IEXEC;
   else if (is_exec (name))
-    permission |= _S_IEXEC;
+    permission |= S_IEXEC;
 
   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
 
@@ -2628,13 +3295,13 @@ fstat (int desc, struct stat * buf)
 {
   HANDLE fh = (HANDLE) _get_osfhandle (desc);
   BY_HANDLE_FILE_INFORMATION info;
-  DWORD fake_inode;
+  unsigned __int64 fake_inode;
   int permission;
 
   switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
     {
     case FILE_TYPE_DISK:
-      buf->st_mode = _S_IFREG;
+      buf->st_mode = S_IFREG;
       if (!GetFileInformationByHandle (fh, &info))
        {
          errno = EACCES;
@@ -2642,12 +3309,12 @@ fstat (int desc, struct stat * buf)
        }
       break;
     case FILE_TYPE_PIPE:
-      buf->st_mode = _S_IFIFO;
+      buf->st_mode = S_IFIFO;
       goto non_disk;
     case FILE_TYPE_CHAR:
     case FILE_TYPE_UNKNOWN:
     default:
-      buf->st_mode = _S_IFCHR;
+      buf->st_mode = S_IFCHR;
     non_disk:
       memset (&info, 0, sizeof (info));
       info.dwFileAttributes = 0;
@@ -2657,7 +3324,7 @@ fstat (int desc, struct stat * buf)
     }
 
   if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-      buf->st_mode = _S_IFDIR;
+      buf->st_mode = S_IFDIR;
 
   buf->st_nlink = info.nNumberOfLinks;
   /* Might as well use file index to fake inode values, but this
@@ -2665,7 +3332,9 @@ fstat (int desc, struct stat * buf)
      all the time (even then there are situations where it is
      not unique).  Reputedly, there are at most 48 bits of info
      (on NTFS, presumably less on FAT). */
-  fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+  fake_inode = info.nFileIndexHigh;
+  fake_inode <<= 32;
+  fake_inode += info.nFileIndexLow;
 
   /* MSVC defines _ino_t to be short; other libc's might not.  */
   if (sizeof (buf->st_ino) == 2)
@@ -2673,14 +3342,20 @@ fstat (int desc, struct stat * buf)
   else
     buf->st_ino = fake_inode;
 
-  /* consider files to belong to current user */
-  buf->st_uid = 0;
-  buf->st_gid = 0;
+  /* Consider files to belong to current user.
+     FIXME: this should use GetSecurityInfo API, but it is only
+     available for _WIN32_WINNT >= 0x501.  */
+  buf->st_uid = dflt_passwd.pw_uid;
+  buf->st_gid = dflt_passwd.pw_gid;
+  strcpy (buf->st_uname, dflt_passwd.pw_name);
+  strcpy (buf->st_gname, dflt_group.gr_name);
 
   buf->st_dev = info.dwVolumeSerialNumber;
   buf->st_rdev = info.dwVolumeSerialNumber;
 
-  buf->st_size = info.nFileSizeLow;
+  buf->st_size = info.nFileSizeHigh;
+  buf->st_size <<= 32;
+  buf->st_size += info.nFileSizeLow;
 
   /* Convert timestamps to Unix format. */
   buf->st_mtime = convert_time (info.ftLastWriteTime);
@@ -2691,22 +3366,22 @@ fstat (int desc, struct stat * buf)
 
   /* determine rwx permissions */
   if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
-    permission = _S_IREAD;
+    permission = S_IREAD;
   else
-    permission = _S_IREAD | _S_IWRITE;
+    permission = S_IREAD | S_IWRITE;
 
   if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-    permission |= _S_IEXEC;
+    permission |= S_IEXEC;
   else
     {
 #if 0 /* no way of knowing the filename */
       char * p = strrchr (name, '.');
       if (p != NULL &&
-         (stricmp (p, ".exe") == 0 ||
-          stricmp (p, ".com") == 0 ||
-          stricmp (p, ".bat") == 0 ||
-          stricmp (p, ".cmd") == 0))
-       permission |= _S_IEXEC;
+         (xstrcasecmp (p, ".exe") == 0 ||
+          xstrcasecmp (p, ".com") == 0 ||
+          xstrcasecmp (p, ".bat") == 0 ||
+          xstrcasecmp (p, ".cmd") == 0))
+       permission |= S_IEXEC;
 #endif
     }
 
@@ -2752,6 +3427,706 @@ utime (const char *name, struct utimbuf *times)
   return 0;
 }
 
+\f
+/* Support for browsing other processes and their attributes.  See
+   process.c for the Lisp bindings.  */
+
+/* Helper wrapper functions.  */
+
+HANDLE WINAPI create_toolhelp32_snapshot(
+    DWORD Flags,
+    DWORD Ignored)
+{
+  static CreateToolhelp32Snapshot_Proc s_pfn_Create_Toolhelp32_Snapshot = NULL;
+
+  if (g_b_init_create_toolhelp32_snapshot == 0)
+    {
+      g_b_init_create_toolhelp32_snapshot = 1;
+      s_pfn_Create_Toolhelp32_Snapshot = (CreateToolhelp32Snapshot_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "CreateToolhelp32Snapshot");
+    }
+  if (s_pfn_Create_Toolhelp32_Snapshot == NULL)
+    {
+      return INVALID_HANDLE_VALUE;
+    }
+  return (s_pfn_Create_Toolhelp32_Snapshot (Flags, Ignored));
+}
+
+BOOL WINAPI process32_first(
+    HANDLE hSnapshot,
+    LPPROCESSENTRY32 lppe)
+{
+  static Process32First_Proc s_pfn_Process32_First = NULL;
+
+  if (g_b_init_process32_first == 0)
+    {
+      g_b_init_process32_first = 1;
+      s_pfn_Process32_First = (Process32First_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "Process32First");
+    }
+  if (s_pfn_Process32_First == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Process32_First (hSnapshot, lppe));
+}
+
+BOOL WINAPI process32_next(
+    HANDLE hSnapshot,
+    LPPROCESSENTRY32 lppe)
+{
+  static Process32Next_Proc s_pfn_Process32_Next = NULL;
+
+  if (g_b_init_process32_next == 0)
+    {
+      g_b_init_process32_next = 1;
+      s_pfn_Process32_Next = (Process32Next_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "Process32Next");
+    }
+  if (s_pfn_Process32_Next == NULL)
+    {
+      return FALSE;
+    }
+  return (s_pfn_Process32_Next (hSnapshot, lppe));
+}
+
+BOOL WINAPI open_thread_token (
+    HANDLE ThreadHandle,
+    DWORD DesiredAccess,
+    BOOL OpenAsSelf,
+    PHANDLE TokenHandle)
+{
+  static OpenThreadToken_Proc s_pfn_Open_Thread_Token = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      SetLastError (ERROR_NOT_SUPPORTED);
+      return FALSE;
+    }
+  if (g_b_init_open_thread_token == 0)
+    {
+      g_b_init_open_thread_token = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Open_Thread_Token =
+        (OpenThreadToken_Proc) GetProcAddress (hm_advapi32, "OpenThreadToken");
+    }
+  if (s_pfn_Open_Thread_Token == NULL)
+    {
+      SetLastError (ERROR_NOT_SUPPORTED);
+      return FALSE;
+    }
+  return (
+      s_pfn_Open_Thread_Token (
+          ThreadHandle,
+          DesiredAccess,
+         OpenAsSelf,
+          TokenHandle)
+      );
+}
+
+BOOL WINAPI impersonate_self (
+    SECURITY_IMPERSONATION_LEVEL ImpersonationLevel)
+{
+  static ImpersonateSelf_Proc s_pfn_Impersonate_Self = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_impersonate_self == 0)
+    {
+      g_b_init_impersonate_self = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Impersonate_Self =
+        (ImpersonateSelf_Proc) GetProcAddress (hm_advapi32, "ImpersonateSelf");
+    }
+  if (s_pfn_Impersonate_Self == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Impersonate_Self (ImpersonationLevel);
+}
+
+BOOL WINAPI revert_to_self (void)
+{
+  static RevertToSelf_Proc s_pfn_Revert_To_Self = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_revert_to_self == 0)
+    {
+      g_b_init_revert_to_self = 1;
+      hm_advapi32 = LoadLibrary ("Advapi32.dll");
+      s_pfn_Revert_To_Self =
+        (RevertToSelf_Proc) GetProcAddress (hm_advapi32, "RevertToSelf");
+    }
+  if (s_pfn_Revert_To_Self == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Revert_To_Self ();
+}
+
+BOOL WINAPI get_process_memory_info (
+    HANDLE h_proc,
+    PPROCESS_MEMORY_COUNTERS mem_counters,
+    DWORD bufsize)
+{
+  static GetProcessMemoryInfo_Proc s_pfn_Get_Process_Memory_Info = NULL;
+  HMODULE hm_psapi = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_process_memory_info == 0)
+    {
+      g_b_init_get_process_memory_info = 1;
+      hm_psapi = LoadLibrary ("Psapi.dll");
+      if (hm_psapi)
+       s_pfn_Get_Process_Memory_Info = (GetProcessMemoryInfo_Proc)
+         GetProcAddress (hm_psapi, "GetProcessMemoryInfo");
+    }
+  if (s_pfn_Get_Process_Memory_Info == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Get_Process_Memory_Info (h_proc, mem_counters, bufsize);
+}
+
+BOOL WINAPI get_process_working_set_size (
+    HANDLE h_proc,
+    DWORD *minrss,
+    DWORD *maxrss)
+{
+  static GetProcessWorkingSetSize_Proc
+    s_pfn_Get_Process_Working_Set_Size = NULL;
+
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_process_working_set_size == 0)
+    {
+      g_b_init_get_process_working_set_size = 1;
+      s_pfn_Get_Process_Working_Set_Size = (GetProcessWorkingSetSize_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "GetProcessWorkingSetSize");
+    }
+  if (s_pfn_Get_Process_Working_Set_Size == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Get_Process_Working_Set_Size (h_proc, minrss, maxrss);
+}
+
+BOOL WINAPI global_memory_status (
+    MEMORYSTATUS *buf)
+{
+  static GlobalMemoryStatus_Proc s_pfn_Global_Memory_Status = NULL;
+
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_global_memory_status == 0)
+    {
+      g_b_init_global_memory_status = 1;
+      s_pfn_Global_Memory_Status = (GlobalMemoryStatus_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "GlobalMemoryStatus");
+    }
+  if (s_pfn_Global_Memory_Status == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Global_Memory_Status (buf);
+}
+
+BOOL WINAPI global_memory_status_ex (
+    MEMORY_STATUS_EX *buf)
+{
+  static GlobalMemoryStatusEx_Proc s_pfn_Global_Memory_Status_Ex = NULL;
+
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_global_memory_status_ex == 0)
+    {
+      g_b_init_global_memory_status_ex = 1;
+      s_pfn_Global_Memory_Status_Ex = (GlobalMemoryStatusEx_Proc)
+       GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                       "GlobalMemoryStatusEx");
+    }
+  if (s_pfn_Global_Memory_Status_Ex == NULL)
+    {
+      return FALSE;
+    }
+  return s_pfn_Global_Memory_Status_Ex (buf);
+}
+
+Lisp_Object
+w32_list_system_processes ()
+{
+  struct gcpro gcpro1;
+  Lisp_Object proclist = Qnil;
+  HANDLE h_snapshot;
+
+  h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
+
+  if (h_snapshot != INVALID_HANDLE_VALUE)
+    {
+      PROCESSENTRY32 proc_entry;
+      DWORD proc_id;
+      BOOL res;
+
+      GCPRO1 (proclist);
+
+      proc_entry.dwSize = sizeof (PROCESSENTRY32);
+      for (res = process32_first (h_snapshot, &proc_entry); res;
+          res = process32_next  (h_snapshot, &proc_entry))
+       {
+         proc_id = proc_entry.th32ProcessID;
+         proclist = Fcons (make_fixnum_or_float (proc_id), proclist);
+       }
+
+      CloseHandle (h_snapshot);
+      UNGCPRO;
+      proclist = Fnreverse (proclist);
+    }
+
+  return proclist;
+}
+
+static int
+enable_privilege (LPCTSTR priv_name, BOOL enable_p, TOKEN_PRIVILEGES *old_priv)
+{
+  TOKEN_PRIVILEGES priv;
+  DWORD priv_size = sizeof (priv);
+  DWORD opriv_size = sizeof (*old_priv);
+  HANDLE h_token = NULL;
+  HANDLE h_thread = GetCurrentThread ();
+  int ret_val = 0;
+  BOOL res;
+
+  res = open_thread_token (h_thread,
+                          TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
+                          FALSE, &h_token);
+  if (!res && GetLastError () == ERROR_NO_TOKEN)
+    {
+      if (impersonate_self (SecurityImpersonation))
+         res = open_thread_token (h_thread,
+                                  TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
+                                  FALSE, &h_token);
+    }
+  if (res)
+    {
+      priv.PrivilegeCount = 1;
+      priv.Privileges[0].Attributes = enable_p ? SE_PRIVILEGE_ENABLED : 0;
+      LookupPrivilegeValue (NULL, priv_name, &priv.Privileges[0].Luid);
+      if (AdjustTokenPrivileges (h_token, FALSE, &priv, priv_size,
+                                old_priv, &opriv_size)
+         && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
+       ret_val = 1;
+    }
+  if (h_token)
+    CloseHandle (h_token);
+
+  return ret_val;
+}
+
+static int
+restore_privilege (TOKEN_PRIVILEGES *priv)
+{
+  DWORD priv_size = sizeof (*priv);
+  HANDLE h_token = NULL;
+  int ret_val = 0;
+
+  if (open_thread_token (GetCurrentThread (),
+                        TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
+                        FALSE, &h_token))
+    {
+      if (AdjustTokenPrivileges (h_token, FALSE, priv, priv_size, NULL, NULL)
+         && GetLastError () != ERROR_NOT_ALL_ASSIGNED)
+       ret_val = 1;
+    }
+  if (h_token)
+    CloseHandle (h_token);
+
+  return ret_val;
+}
+
+static Lisp_Object
+ltime (time_sec, time_usec)
+     long time_sec, time_usec;
+{
+  return list3 (make_number ((time_sec >> 16) & 0xffff),
+               make_number (time_sec & 0xffff),
+               make_number (time_usec));
+}
+
+static int
+process_times (h_proc, ctime, etime, stime, utime, pcpu)
+     HANDLE h_proc;
+     Lisp_Object *ctime, *etime, *stime, *utime;
+     double *pcpu;
+{
+  FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
+  long ctime_sec, ctime_usec, stime_sec, stime_usec, utime_sec, utime_usec;
+  long etime_sec, etime_usec;
+  long double tem1, tem2, tem;
+
+  if (!h_proc
+      || !get_process_times_fn
+      || !(*get_process_times_fn)(h_proc, &ft_creation, &ft_exit,
+                                 &ft_kernel, &ft_user))
+    return 0;
+
+  GetSystemTimeAsFileTime (&ft_current);
+
+  tem1 = convert_time_raw (ft_kernel) * 0.1L;
+  stime_usec = fmodl (tem1, 1000000.0L);
+  stime_sec = tem1 * 0.000001L;
+  *stime = ltime (stime_sec, stime_usec);
+  tem2 = convert_time_raw (ft_user) * 0.1L;
+  utime_usec = fmodl (tem2, 1000000.0L);
+  utime_sec = tem2 * 0.000001L;
+  *utime = ltime (utime_sec, utime_usec);
+  tem = convert_time_raw (ft_creation);
+  /* Process no 4 (System) returns zero creation time.  */
+  if (tem)
+    tem = (tem - utc_base) * 0.1;
+  ctime_usec = fmodl (tem, 1000000.0L);
+  ctime_sec = tem * 0.000001L;
+  *ctime = ltime (ctime_sec, ctime_usec);
+  if (tem)
+    tem = (convert_time_raw (ft_current) - utc_base) * 0.1L - tem;
+  etime_usec = fmodl (tem, 1000000.0L);
+  etime_sec = tem * 0.000001L;
+  *etime = ltime (etime_sec, etime_usec);
+
+  if (tem)
+    {
+      *pcpu = 100.0 * (tem1 + tem2) / tem;
+      if (*pcpu > 100)
+       *pcpu = 100.0;
+    }
+  else
+    *pcpu = 0;
+
+  return 1;
+}
+
+Lisp_Object
+w32_system_process_attributes (pid)
+     Lisp_Object pid;
+{
+  struct gcpro gcpro1, gcpro2, gcpro3;
+  Lisp_Object attrs = Qnil;
+  Lisp_Object cmd_str, decoded_cmd, tem;
+  HANDLE h_snapshot, h_proc;
+  DWORD proc_id;
+  int found_proc = 0;
+  char uname[UNLEN+1], gname[GNLEN+1], domain[1025];
+  DWORD ulength = sizeof (uname), dlength = sizeof (domain), needed;
+  DWORD glength = sizeof (gname);
+  HANDLE token = NULL;
+  SID_NAME_USE user_type;
+  unsigned char *buf = NULL;
+  DWORD blen = 0;
+  TOKEN_USER user_token;
+  TOKEN_PRIMARY_GROUP group_token;
+  int euid;
+  int egid;
+  DWORD sess;
+  PROCESS_MEMORY_COUNTERS mem;
+  PROCESS_MEMORY_COUNTERS_EX mem_ex;
+  DWORD minrss, maxrss;
+  MEMORYSTATUS memst;
+  MEMORY_STATUS_EX memstex;
+  double totphys = 0.0;
+  Lisp_Object ctime, stime, utime, etime;
+  double pcpu;
+  BOOL result = FALSE;
+
+  CHECK_NUMBER_OR_FLOAT (pid);
+  proc_id = FLOATP (pid) ? XFLOAT_DATA (pid) : XINT (pid);
+
+  h_snapshot = create_toolhelp32_snapshot (TH32CS_SNAPPROCESS, 0);
+
+  GCPRO3 (attrs, decoded_cmd, tem);
+
+  if (h_snapshot != INVALID_HANDLE_VALUE)
+    {
+      PROCESSENTRY32 pe;
+      BOOL res;
+
+      pe.dwSize = sizeof (PROCESSENTRY32);
+      for (res = process32_first (h_snapshot, &pe); res;
+          res = process32_next  (h_snapshot, &pe))
+       {
+         if (proc_id == pe.th32ProcessID)
+           {
+             if (proc_id == 0)
+               decoded_cmd = build_string ("Idle");
+             else
+               {
+                 /* Decode the command name from locale-specific
+                    encoding.  */
+                 cmd_str = make_unibyte_string (pe.szExeFile,
+                                                strlen (pe.szExeFile));
+                 decoded_cmd =
+                   code_convert_string_norecord (cmd_str,
+                                                 Vlocale_coding_system, 0);
+               }
+             attrs = Fcons (Fcons (Qcomm, decoded_cmd), attrs);
+             attrs = Fcons (Fcons (Qppid,
+                                   make_fixnum_or_float (pe.th32ParentProcessID)),
+                            attrs);
+             attrs = Fcons (Fcons (Qpri, make_number (pe.pcPriClassBase)),
+                            attrs);
+             attrs = Fcons (Fcons (Qthcount,
+                                   make_fixnum_or_float (pe.cntThreads)),
+                            attrs);
+             found_proc = 1;
+             break;
+           }
+       }
+
+      CloseHandle (h_snapshot);
+    }
+
+  if (!found_proc)
+    {
+      UNGCPRO;
+      return Qnil;
+    }
+
+  h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                       FALSE, proc_id);
+  /* If we were denied a handle to the process, try again after
+     enabling the SeDebugPrivilege in our process.  */
+  if (!h_proc)
+    {
+      TOKEN_PRIVILEGES priv_current;
+
+      if (enable_privilege (SE_DEBUG_NAME, TRUE, &priv_current))
+       {
+         h_proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+                               FALSE, proc_id);
+         restore_privilege (&priv_current);
+         revert_to_self ();
+       }
+    }
+  if (h_proc)
+    {
+      result = open_process_token (h_proc, TOKEN_QUERY, &token);
+      if (result)
+       {
+         result = get_token_information (token, TokenUser, NULL, 0, &blen);
+         if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
+           {
+             buf = xmalloc (blen);
+             result = get_token_information (token, TokenUser,
+                                             (LPVOID)buf, blen, &needed);
+             if (result)
+               {
+                 memcpy (&user_token, buf, sizeof (user_token));
+                 if (!w32_cached_id (user_token.User.Sid, &euid, uname))
+                   {
+                     euid = get_rid (user_token.User.Sid);
+                     result = lookup_account_sid (NULL, user_token.User.Sid,
+                                                  uname, &ulength,
+                                                  domain, &dlength,
+                                                  &user_type);
+                     if (result)
+                       w32_add_to_cache (user_token.User.Sid, euid, uname);
+                     else
+                       {
+                         strcpy (uname, "unknown");
+                         result = TRUE;
+                       }
+                   }
+                 ulength = strlen (uname);
+               }
+           }
+       }
+      if (result)
+       {
+         /* Determine a reasonable euid and gid values.  */
+         if (xstrcasecmp ("administrator", uname) == 0)
+           {
+             euid = 500;       /* well-known Administrator uid */
+             egid = 513;       /* well-known None gid */
+           }
+         else
+           {
+             /* Get group id and name.  */
+             result = get_token_information (token, TokenPrimaryGroup,
+                                             (LPVOID)buf, blen, &needed);
+             if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
+               {
+                 buf = xrealloc (buf, blen = needed);
+                 result = get_token_information (token, TokenPrimaryGroup,
+                                                 (LPVOID)buf, blen, &needed);
+               }
+             if (result)
+               {
+                 memcpy (&group_token, buf, sizeof (group_token));
+                 if (!w32_cached_id (group_token.PrimaryGroup, &egid, gname))
+                   {
+                     egid = get_rid (group_token.PrimaryGroup);
+                     dlength = sizeof (domain);
+                     result =
+                       lookup_account_sid (NULL, group_token.PrimaryGroup,
+                                           gname, &glength, NULL, &dlength,
+                                           &user_type);
+                     if (result)
+                       w32_add_to_cache (group_token.PrimaryGroup,
+                                         egid, gname);
+                     else
+                       {
+                         strcpy (gname, "None");
+                         result = TRUE;
+                       }
+                   }
+                 glength = strlen (gname);
+               }
+           }
+       }
+      if (buf)
+       xfree (buf);
+    }
+  if (!result)
+    {
+      if (!is_windows_9x ())
+       {
+         /* We couldn't open the process token, presumably because of
+            insufficient access rights.  Assume this process is run
+            by the system.  */
+         strcpy (uname, "SYSTEM");
+         strcpy (gname, "None");
+         euid = 18;    /* SYSTEM */
+         egid = 513;   /* None */
+         glength = strlen (gname);
+         ulength = strlen (uname);
+       }
+      /* If we are running under Windows 9X, where security calls are
+        not supported, we assume all processes are run by the current
+        user.  */
+      else if (GetUserName (uname, &ulength))
+       {
+         if (xstrcasecmp ("administrator", uname) == 0)
+           euid = 0;
+         else
+           euid = 123;
+         egid = euid;
+         strcpy (gname, "None");
+         glength = strlen (gname);
+         ulength = strlen (uname);
+       }
+      else
+       {
+         euid = 123;
+         egid = 123;
+         strcpy (uname, "administrator");
+         ulength = strlen (uname);
+         strcpy (gname, "None");
+         glength = strlen (gname);
+       }
+      if (token)
+       CloseHandle (token);
+    }
+
+  attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (euid)), attrs);
+  tem = make_unibyte_string (uname, ulength);
+  attrs = Fcons (Fcons (Quser,
+                        code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
+                attrs);
+  attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (egid)), attrs);
+  tem = make_unibyte_string (gname, glength);
+  attrs = Fcons (Fcons (Qgroup,
+                        code_convert_string_norecord (tem, Vlocale_coding_system, 0)),
+                attrs);
+
+  if (global_memory_status_ex (&memstex))
+#if __GNUC__ || (defined (_MSC_VER) && _MSC_VER >= 1300)
+    totphys = memstex.ullTotalPhys / 1024.0;
+#else
+  /* Visual Studio 6 cannot convert an unsigned __int64 type to
+     double, so we need to do this for it...  */
+    {
+      DWORD tot_hi = memstex.ullTotalPhys >> 32;
+      DWORD tot_md = (memstex.ullTotalPhys & 0x00000000ffffffff) >> 10;
+      DWORD tot_lo = memstex.ullTotalPhys % 1024;
+
+      totphys = tot_hi * 4194304.0 + tot_md + tot_lo / 1024.0;
+    }
+#endif /* __GNUC__ || _MSC_VER >= 1300 */
+  else if (global_memory_status (&memst))
+    totphys = memst.dwTotalPhys / 1024.0;
+
+  if (h_proc
+      && get_process_memory_info (h_proc, (PROCESS_MEMORY_COUNTERS *)&mem_ex,
+                                 sizeof (mem_ex)))
+    {
+      DWORD rss = mem_ex.WorkingSetSize / 1024;
+
+      attrs = Fcons (Fcons (Qmajflt,
+                           make_fixnum_or_float (mem_ex.PageFaultCount)),
+                    attrs);
+      attrs = Fcons (Fcons (Qvsize,
+                           make_fixnum_or_float (mem_ex.PrivateUsage / 1024)),
+                    attrs);
+      attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
+      if (totphys)
+       attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
+    }
+  else if (h_proc
+          && get_process_memory_info (h_proc, &mem, sizeof (mem)))
+    {
+      DWORD rss = mem_ex.WorkingSetSize / 1024;
+
+      attrs = Fcons (Fcons (Qmajflt,
+                           make_fixnum_or_float (mem.PageFaultCount)),
+                    attrs);
+      attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (rss)), attrs);
+      if (totphys)
+       attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
+    }
+  else if (h_proc
+          && get_process_working_set_size (h_proc, &minrss, &maxrss))
+    {
+      DWORD rss = maxrss / 1024;
+
+      attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (maxrss / 1024)), attrs);
+      if (totphys)
+       attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
+    }
+
+  if (process_times (h_proc, &ctime, &etime, &stime, &utime, &pcpu))
+    {
+      attrs = Fcons (Fcons (Qutime, utime), attrs);
+      attrs = Fcons (Fcons (Qstime, stime), attrs);
+      attrs = Fcons (Fcons (Qstart, ctime), attrs);
+      attrs = Fcons (Fcons (Qetime, etime), attrs);
+      attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
+    }
+
+  /* FIXME: Retrieve command line by walking the PEB of the process.  */
+
+  if (h_proc)
+    CloseHandle (h_proc);
+  UNGCPRO;
+  return attrs;
+}
+
+\f
 #ifdef HAVE_SOCKETS
 
 /* Wrappers for  winsock functions to map between our file descriptors
@@ -2911,7 +4286,7 @@ init_winsock (int load_now)
 
 int h_errno = 0;
 
-/* function to set h_errno for compatability; map winsock error codes to
+/* function to set h_errno for compatibility; map winsock error codes to
    normal system codes where they overlap (non-overlapping definitions
    are already in <sys/socket.h> */
 static void
@@ -3662,10 +5037,10 @@ _sys_read_ahead (int fd)
   if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
     return STATUS_READ_ERROR;
 
-  if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0
+  if ((fd_info[fd].flags & (FILE_PIPE | FILE_SERIAL | FILE_SOCKET)) == 0
       || (fd_info[fd].flags & FILE_READ) == 0)
     {
-      DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd));
+      DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe, serial port, or socket!\n", fd));
       abort ();
     }
 
@@ -3679,7 +5054,7 @@ _sys_read_ahead (int fd)
         reporting that input is available; we need this because Windows 95
         connects DOS programs to pipes by making the pipe appear to be
         the normal console stdout - as a result most DOS programs will
-        write to stdout without buffering, ie.  one character at a
+        write to stdout without buffering, ie. one character at a
         time.  Even some W32 programs do this - "dir" in a command
         shell on NT is very slow if we don't do this. */
       if (rc > 0)
@@ -3695,6 +5070,29 @@ _sys_read_ahead (int fd)
              Sleep (0);
        }
     }
+  else if (fd_info[fd].flags & FILE_SERIAL)
+    {
+      HANDLE hnd = fd_info[fd].hnd;
+      OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
+      COMMTIMEOUTS ct;
+
+      /* Configure timeouts for blocking read.  */
+      if (!GetCommTimeouts (hnd, &ct))
+       return STATUS_READ_ERROR;
+      ct.ReadIntervalTimeout           = 0;
+      ct.ReadTotalTimeoutMultiplier    = 0;
+      ct.ReadTotalTimeoutConstant      = 0;
+      if (!SetCommTimeouts (hnd, &ct))
+       return STATUS_READ_ERROR;
+
+      if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
+       {
+         if (GetLastError () != ERROR_IO_PENDING)
+           return STATUS_READ_ERROR;
+         if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
+           return STATUS_READ_ERROR;
+       }
+    }
 #ifdef HAVE_SOCKETS
   else if (fd_info[fd].flags & FILE_SOCKET)
     {
@@ -3766,7 +5164,7 @@ sys_read (int fd, char * buffer, unsigned int count)
       return -1;
     }
 
-  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
     {
       child_process *cp = fd_info[fd].cp;
 
@@ -3837,6 +5235,52 @@ sys_read (int fd, char * buffer, unsigned int count)
              if (to_read > 0)
                nchars += _read (fd, buffer, to_read);
            }
+         else if (fd_info[fd].flags & FILE_SERIAL)
+           {
+             HANDLE hnd = fd_info[fd].hnd;
+             OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read;
+             DWORD err = 0;
+             int rc = 0;
+             COMMTIMEOUTS ct;
+
+             if (count > 0)
+               {
+                 /* Configure timeouts for non-blocking read.  */
+                 if (!GetCommTimeouts (hnd, &ct))
+                   {
+                     errno = EIO;
+                     return -1;
+                   }
+                 ct.ReadIntervalTimeout         = MAXDWORD;
+                 ct.ReadTotalTimeoutMultiplier  = 0;
+                 ct.ReadTotalTimeoutConstant    = 0;
+                 if (!SetCommTimeouts (hnd, &ct))
+                   {
+                     errno = EIO;
+                     return -1;
+                   }
+
+                 if (!ResetEvent (ovl->hEvent))
+                   {
+                     errno = EIO;
+                     return -1;
+                   }
+                 if (!ReadFile (hnd, buffer, count, (DWORD*) &rc, ovl))
+                   {
+                     if (GetLastError () != ERROR_IO_PENDING)
+                       {
+                         errno = EIO;
+                         return -1;
+                       }
+                     if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
+                       {
+                         errno = EIO;
+                         return -1;
+                       }
+                   }
+                 nchars += rc;
+               }
+           }
 #ifdef HAVE_SOCKETS
          else /* FILE_SOCKET */
            {
@@ -3898,6 +5342,9 @@ sys_read (int fd, char * buffer, unsigned int count)
   return nchars;
 }
 
+/* From w32xfns.c */
+extern HANDLE interrupt_handle;
+
 /* For now, don't bother with a non-blocking mode */
 int
 sys_write (int fd, const void * buffer, unsigned int count)
@@ -3910,7 +5357,7 @@ sys_write (int fd, const void * buffer, unsigned int count)
       return -1;
     }
 
-  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET | FILE_SERIAL))
     {
       if ((fd_info[fd].flags & FILE_WRITE) == 0)
        {
@@ -3951,6 +5398,42 @@ sys_write (int fd, const void * buffer, unsigned int count)
        }
     }
 
+  if (fd < MAXDESC && fd_info[fd].flags & FILE_SERIAL)
+    {
+      HANDLE hnd = (HANDLE) _get_osfhandle (fd);
+      OVERLAPPED *ovl = &fd_info[fd].cp->ovl_write;
+      HANDLE wait_hnd[2] = { interrupt_handle, ovl->hEvent };
+      DWORD active = 0;
+
+      if (!WriteFile (hnd, buffer, count, (DWORD*) &nchars, ovl))
+       {
+         if (GetLastError () != ERROR_IO_PENDING)
+           {
+             errno = EIO;
+             return -1;
+           }
+         if (detect_input_pending ())
+           active = MsgWaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE,
+                                               QS_ALLINPUT);
+         else
+           active = WaitForMultipleObjects (2, wait_hnd, FALSE, INFINITE);
+         if (active == WAIT_OBJECT_0)
+           { /* User pressed C-g, cancel write, then leave.  Don't bother
+                cleaning up as we may only get stuck in buggy drivers.  */
+             PurgeComm (hnd, PURGE_TXABORT | PURGE_TXCLEAR);
+             CancelIo (hnd);
+             errno = EIO;
+             return -1;
+           }
+         if (active == WAIT_OBJECT_0 + 1
+             && !GetOverlappedResult (hnd, ovl, (DWORD*) &nchars, TRUE))
+           {
+             errno = EIO;
+             return -1;
+           }
+       }
+    }
+  else
 #ifdef HAVE_SOCKETS
   if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
     {
@@ -4195,11 +5678,223 @@ globals_of_w32 ()
   g_b_init_get_token_information = 0;
   g_b_init_lookup_account_sid = 0;
   g_b_init_get_sid_identifier_authority = 0;
+  g_b_init_get_sid_sub_authority = 0;
+  g_b_init_get_sid_sub_authority_count = 0;
+  g_b_init_get_file_security = 0;
+  g_b_init_get_security_descriptor_owner = 0;
+  g_b_init_get_security_descriptor_group = 0;
+  g_b_init_is_valid_sid = 0;
+  g_b_init_create_toolhelp32_snapshot = 0;
+  g_b_init_process32_first = 0;
+  g_b_init_process32_next = 0;
+  g_b_init_open_thread_token = 0;
+  g_b_init_impersonate_self = 0;
+  g_b_init_revert_to_self = 0;
+  g_b_init_get_process_memory_info = 0;
+  g_b_init_get_process_working_set_size = 0;
+  g_b_init_global_memory_status = 0;
+  g_b_init_global_memory_status_ex = 0;
+  g_b_init_equal_sid = 0;
+  g_b_init_copy_sid = 0;
+  g_b_init_get_length_sid = 0;
   /* The following sets a handler for shutdown notifications for
      console apps. This actually applies to Emacs in both console and
      GUI modes, since we had to fool windows into thinking emacs is a
      console application to get console mode to work.  */
   SetConsoleCtrlHandler(shutdown_handler, TRUE);
+
+  /* "None" is the default group name on standalone workstations.  */
+  strcpy (dflt_group_name, "None");
+}
+
+/* For make-serial-process  */
+int serial_open (char *port)
+{
+  HANDLE hnd;
+  child_process *cp;
+  int fd = -1;
+
+  hnd = CreateFile (port, GENERIC_READ | GENERIC_WRITE, 0, 0,
+                   OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+  if (hnd == INVALID_HANDLE_VALUE)
+    error ("Could not open %s", port);
+  fd = (int) _open_osfhandle ((int) hnd, 0);
+  if (fd == -1)
+    error ("Could not open %s", port);
+
+  cp = new_child ();
+  if (!cp)
+    error ("Could not create child process");
+  cp->fd = fd;
+  cp->status = STATUS_READ_ACKNOWLEDGED;
+  fd_info[ fd ].hnd = hnd;
+  fd_info[ fd ].flags |=
+    FILE_READ | FILE_WRITE | FILE_BINARY | FILE_SERIAL;
+  if (fd_info[ fd ].cp != NULL)
+    {
+      error ("fd_info[fd = %d] is already in use", fd);
+    }
+  fd_info[ fd ].cp = cp;
+  cp->ovl_read.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+  if (cp->ovl_read.hEvent == NULL)
+      error ("Could not create read event");
+  cp->ovl_write.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
+  if (cp->ovl_write.hEvent == NULL)
+      error ("Could not create write event");
+
+  return fd;
+}
+
+/* For serial-process-configure  */
+void
+serial_configure (struct Lisp_Process *p,
+                     Lisp_Object contact)
+{
+  Lisp_Object childp2 = Qnil;
+  Lisp_Object tem = Qnil;
+  HANDLE hnd;
+  DCB dcb;
+  COMMTIMEOUTS ct;
+  char summary[4] = "???"; /* This usually becomes "8N1".  */
+
+  if ((fd_info[ p->outfd ].flags & FILE_SERIAL) == 0)
+    error ("Not a serial process");
+  hnd = fd_info[ p->outfd ].hnd;
+
+  childp2 = Fcopy_sequence (p->childp);
+
+  /* Initialize timeouts for blocking read and blocking write.  */
+  if (!GetCommTimeouts (hnd, &ct))
+    error ("GetCommTimeouts() failed");
+  ct.ReadIntervalTimeout        = 0;
+  ct.ReadTotalTimeoutMultiplier         = 0;
+  ct.ReadTotalTimeoutConstant   = 0;
+  ct.WriteTotalTimeoutMultiplier = 0;
+  ct.WriteTotalTimeoutConstant  = 0;
+  if (!SetCommTimeouts (hnd, &ct))
+    error ("SetCommTimeouts() failed");
+  /* Read port attributes and prepare default configuration.  */
+  memset (&dcb, 0, sizeof (dcb));
+  dcb.DCBlength = sizeof (DCB);
+  if (!GetCommState (hnd, &dcb))
+    error ("GetCommState() failed");
+  dcb.fBinary      = TRUE;
+  dcb.fNull        = FALSE;
+  dcb.fAbortOnError = FALSE;
+  /* dcb.XonLim and dcb.XoffLim are set by GetCommState() */
+  dcb.ErrorChar            = 0;
+  dcb.EofChar      = 0;
+  dcb.EvtChar       = 0;
+
+  /* Configure speed.  */
+  if (!NILP (Fplist_member (contact, QCspeed)))
+    tem = Fplist_get (contact, QCspeed);
+  else
+    tem = Fplist_get (p->childp, QCspeed);
+  CHECK_NUMBER (tem);
+  dcb.BaudRate = XINT (tem);
+  childp2 = Fplist_put (childp2, QCspeed, tem);
+
+  /* Configure bytesize.  */
+  if (!NILP (Fplist_member (contact, QCbytesize)))
+    tem = Fplist_get (contact, QCbytesize);
+  else
+    tem = Fplist_get (p->childp, QCbytesize);
+  if (NILP (tem))
+    tem = make_number (8);
+  CHECK_NUMBER (tem);
+  if (XINT (tem) != 7 && XINT (tem) != 8)
+    error (":bytesize must be nil (8), 7, or 8");
+  dcb.ByteSize = XINT (tem);
+  summary[0] = XINT (tem) + '0';
+  childp2 = Fplist_put (childp2, QCbytesize, tem);
+
+  /* Configure parity.  */
+  if (!NILP (Fplist_member (contact, QCparity)))
+    tem = Fplist_get (contact, QCparity);
+  else
+    tem = Fplist_get (p->childp, QCparity);
+  if (!NILP (tem) && !EQ (tem, Qeven) && !EQ (tem, Qodd))
+    error (":parity must be nil (no parity), `even', or `odd'");
+  dcb.fParity = FALSE;
+  dcb.Parity = NOPARITY;
+  dcb.fErrorChar = FALSE;
+  if (NILP (tem))
+    {
+      summary[1] = 'N';
+    }
+  else if (EQ (tem, Qeven))
+    {
+      summary[1] = 'E';
+      dcb.fParity = TRUE;
+      dcb.Parity = EVENPARITY;
+      dcb.fErrorChar = TRUE;
+    }
+  else if (EQ (tem, Qodd))
+    {
+      summary[1] = 'O';
+      dcb.fParity = TRUE;
+      dcb.Parity = ODDPARITY;
+      dcb.fErrorChar = TRUE;
+    }
+  childp2 = Fplist_put (childp2, QCparity, tem);
+
+  /* Configure stopbits.  */
+  if (!NILP (Fplist_member (contact, QCstopbits)))
+    tem = Fplist_get (contact, QCstopbits);
+  else
+    tem = Fplist_get (p->childp, QCstopbits);
+  if (NILP (tem))
+    tem = make_number (1);
+  CHECK_NUMBER (tem);
+  if (XINT (tem) != 1 && XINT (tem) != 2)
+    error (":stopbits must be nil (1 stopbit), 1, or 2");
+  summary[2] = XINT (tem) + '0';
+  if (XINT (tem) == 1)
+    dcb.StopBits = ONESTOPBIT;
+  else if (XINT (tem) == 2)
+    dcb.StopBits = TWOSTOPBITS;
+  childp2 = Fplist_put (childp2, QCstopbits, tem);
+
+  /* Configure flowcontrol.  */
+  if (!NILP (Fplist_member (contact, QCflowcontrol)))
+    tem = Fplist_get (contact, QCflowcontrol);
+  else
+    tem = Fplist_get (p->childp, QCflowcontrol);
+  if (!NILP (tem) && !EQ (tem, Qhw) && !EQ (tem, Qsw))
+    error (":flowcontrol must be nil (no flowcontrol), `hw', or `sw'");
+  dcb.fOutxCtsFlow     = FALSE;
+  dcb.fOutxDsrFlow     = FALSE;
+  dcb.fDtrControl      = DTR_CONTROL_DISABLE;
+  dcb.fDsrSensitivity  = FALSE;
+  dcb.fTXContinueOnXoff        = FALSE;
+  dcb.fOutX            = FALSE;
+  dcb.fInX             = FALSE;
+  dcb.fRtsControl      = RTS_CONTROL_DISABLE;
+  dcb.XonChar          = 17; /* Control-Q  */
+  dcb.XoffChar         = 19; /* Control-S  */
+  if (NILP (tem))
+    {
+      /* Already configured.  */
+    }
+  else if (EQ (tem, Qhw))
+    {
+      dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
+      dcb.fOutxCtsFlow = TRUE;
+    }
+  else if (EQ (tem, Qsw))
+    {
+      dcb.fOutX = TRUE;
+      dcb.fInX = TRUE;
+    }
+  childp2 = Fplist_put (childp2, QCflowcontrol, tem);
+
+  /* Activate configuration.  */
+  if (!SetCommState (hnd, &dcb))
+    error ("SetCommState() failed");
+
+  childp2 = Fplist_put (childp2, QCsummary, build_string (summary));
+  p->childp = childp2;
 }
 
 /* end of w32.c */