]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Clean up EMACS_INT/int usage in doc.c and doprnt.c.
[gnu-emacs] / src / w32.c
index a04de3032e1ba6395ce178d871325c2536bc7a5b..4024ed9512e56be53c535c07989b5697f9408fd3 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, 2008 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+                 2007, 2008, 2009, 2010  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,15 +15,15 @@ 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 */
 #include <stdlib.h>
 #include <stdio.h>
+#include <float.h>     /* for DBL_EPSILON */
 #include <io.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -33,6 +33,8 @@ Boston, MA 02110-1301, USA.
 #include <sys/time.h>
 #include <sys/utime.h>
 #include <mbstring.h>  /* for _mbspbrk */
+#include <math.h>
+#include <setjmp.h>
 
 /* must include CRT headers *before* config.h */
 
@@ -73,10 +75,46 @@ 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>
 
-#ifdef HAVE_SOCKETS    /* TCP connection support, if kernel can do it */
+#include <tlhelp32.h>
+#include <psapi.h>
+#include <w32api.h>
+#if !defined(__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
+/* This either is not in psapi.h or guarded by higher value of
+   _WIN32_WINNT than what we use.  w32api suplied with MinGW 3.15
+   defines it in psapi.h  */
+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;
+#endif
+
+/* TCP connection support.  */
 #include <sys/socket.h>
 #undef socket
 #undef bind
@@ -95,21 +133,29 @@ Boston, MA 02110-1301, USA.
 #undef accept
 #undef recvfrom
 #undef sendto
-#endif
 
 #include "w32.h"
 #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"
 
 typedef HRESULT (WINAPI * ShGetFolderPath_fn)
   (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *);
 
-void globals_of_w32 ();
+void globals_of_w32 (void);
+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
@@ -129,6 +175,25 @@ 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;
+static BOOL g_b_init_get_native_system_info;
+static BOOL g_b_init_get_system_times;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -157,8 +222,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,
@@ -175,19 +242,80 @@ typedef PDWORD (WINAPI * GetSidSubAuthority_Proc) (
     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);
+typedef void (WINAPI * GetNativeSystemInfo_Proc) (
+    LPSYSTEM_INFO lpSystemInfo);
+typedef BOOL (WINAPI * GetSystemTimes_Proc) (
+    LPFILETIME lpIdleTime,
+    LPFILETIME lpKernelTime,
+    LPFILETIME lpUserTime);
+
 
 
   /* ** A utility function ** */
 static BOOL
-is_windows_9x ()
+is_windows_9x (void)
 {
   static BOOL s_b_ret=0;
   OSVERSIONINFO os_ver;
   if (g_b_init_is_windows_9x == 0)
     {
       g_b_init_is_windows_9x = 1;
-      ZeroMemory(&os_ver, sizeof(OSVERSIONINFO));
-      os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+      ZeroMemory (&os_ver, sizeof (OSVERSIONINFO));
+      os_ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
       if (GetVersionEx (&os_ver))
         {
           s_b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
@@ -200,12 +328,12 @@ is_windows_9x ()
    Returns a list of three integers if the times are provided by the OS
    (NT derivatives), otherwise it returns the result of current-time. */
 Lisp_Object
-w32_get_internal_run_time ()
+w32_get_internal_run_time (void)
 {
   if (get_process_times_fn)
     {
       FILETIME create, exit, kernel, user;
-      HANDLE proc = GetCurrentProcess();
+      HANDLE proc = GetCurrentProcess ();
       if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
         {
           LARGE_INTEGER user_int, kernel_int, total;
@@ -415,12 +543,236 @@ PUCHAR WINAPI get_sid_sub_authority_count (
   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
   supported in Windows NT / 2k / XP
 */
 
+void WINAPI get_native_system_info (
+    LPSYSTEM_INFO lpSystemInfo)
+{
+  static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
+  if (is_windows_9x () != TRUE)
+    {
+      if (g_b_init_get_native_system_info == 0)
+       {
+         g_b_init_get_native_system_info = 1;
+         s_pfn_Get_Native_System_Info =
+           (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                     "GetNativeSystemInfo");
+       }
+      if (s_pfn_Get_Native_System_Info != NULL)
+       s_pfn_Get_Native_System_Info (lpSystemInfo);
+    }
+  else
+    lpSystemInfo->dwNumberOfProcessors = -1;
+}
+
+BOOL WINAPI get_system_times (
+    LPFILETIME lpIdleTime,
+    LPFILETIME lpKernelTime,
+    LPFILETIME lpUserTime)
+{
+  static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_system_times == 0)
+    {
+      g_b_init_get_system_times = 1;
+      s_pfn_Get_System_times =
+       (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                            "GetSystemTimes");
+    }
+  if (s_pfn_Get_System_times == NULL)
+    return FALSE;
+  return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
+}
 \f
 /* Equivalent of strerror for W32 error codes.  */
 char *
@@ -481,68 +833,203 @@ getwd (char *dir)
 #endif
 }
 
-#ifndef HAVE_SOCKETS
-/* Emulate gethostname.  */
-int
-gethostname (char *buffer, int size)
+/* Emulate getloadavg.  */
+
+struct load_sample {
+  time_t sample_time;
+  ULONGLONG idle;
+  ULONGLONG kernel;
+  ULONGLONG user;
+};
+
+/* Number of processors on this machine.  */
+static unsigned num_of_processors;
+
+/* We maintain 1-sec samples for the last 16 minutes in a circular buffer.  */
+static struct load_sample samples[16*60];
+static int first_idx = -1, last_idx = -1;
+static int max_idx = sizeof (samples) / sizeof (samples[0]);
+
+static int
+buf_next (int from)
 {
-  /* NT only allows small host names, so the buffer is
-     certainly large enough.  */
-  return !GetComputerName (buffer, &size);
+  int next_idx = from + 1;
+
+  if (next_idx >= max_idx)
+    next_idx = 0;
+
+  return next_idx;
+}
+
+static int
+buf_prev (int from)
+{
+  int prev_idx = from - 1;
+
+  if (prev_idx < 0)
+    prev_idx = max_idx - 1;
+
+  return prev_idx;
+}
+
+static void
+sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
+{
+  SYSTEM_INFO sysinfo;
+  FILETIME ft_idle, ft_user, ft_kernel;
+
+  /* Initialize the number of processors on this machine.  */
+  if (num_of_processors <= 0)
+    {
+      get_native_system_info (&sysinfo);
+      num_of_processors = sysinfo.dwNumberOfProcessors;
+      if (num_of_processors <= 0)
+       {
+         GetSystemInfo (&sysinfo);
+         num_of_processors = sysinfo.dwNumberOfProcessors;
+       }
+      if (num_of_processors <= 0)
+       num_of_processors = 1;
+    }
+
+  /* TODO: Take into account threads that are ready to run, by
+     sampling the "\System\Processor Queue Length" performance
+     counter.  The code below accounts only for threads that are
+     actually running.  */
+
+  if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
+    {
+      ULARGE_INTEGER uidle, ukernel, uuser;
+
+      memcpy (&uidle, &ft_idle, sizeof (ft_idle));
+      memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
+      memcpy (&uuser, &ft_user, sizeof (ft_user));
+      *idle = uidle.QuadPart;
+      *kernel = ukernel.QuadPart;
+      *user = uuser.QuadPart;
+    }
+  else
+    {
+      *idle = 0;
+      *kernel = 0;
+      *user = 0;
+    }
+}
+
+/* Produce the load average for a given time interval, using the
+   samples in the samples[] array.  WHICH can be 0, 1, or 2, meaning
+   1-minute, 5-minute, or 15-minute average, respectively. */
+static double
+getavg (int which)
+{
+  double retval = -1.0;
+  double tdiff;
+  int idx;
+  double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
+  time_t now = samples[last_idx].sample_time;
+
+  if (first_idx != last_idx)
+    {
+      for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
+       {
+         tdiff = difftime (now, samples[idx].sample_time);
+         if (tdiff >= span - 2*DBL_EPSILON*now)
+           {
+             long double sys =
+               samples[last_idx].kernel + samples[last_idx].user
+               - (samples[idx].kernel + samples[idx].user);
+             long double idl = samples[last_idx].idle - samples[idx].idle;
+
+             retval = (1.0 - idl / sys) * num_of_processors;
+             break;
+           }
+         if (idx == first_idx)
+           break;
+       }
+    }
+
+  return retval;
 }
-#endif /* HAVE_SOCKETS */
 
-/* Emulate getloadavg.  */
 int
 getloadavg (double loadavg[], int nelem)
 {
-  int i;
+  int elem;
+  ULONGLONG idle, kernel, user;
+  time_t now = time (NULL);
+
+  /* Store another sample.  We ignore samples that are less than 1 sec
+     apart.  */
+  if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
+    {
+      sample_system_load (&idle, &kernel, &user);
+      last_idx = buf_next (last_idx);
+      samples[last_idx].sample_time = now;
+      samples[last_idx].idle = idle;
+      samples[last_idx].kernel = kernel;
+      samples[last_idx].user = user;
+      /* If the buffer has more that 15 min worth of samples, discard
+        the old ones.  */
+      if (first_idx == -1)
+       first_idx = last_idx;
+      while (first_idx != last_idx
+            && (difftime (now, samples[first_idx].sample_time)
+                >= 15.0*60 + 2*DBL_EPSILON*now))
+       first_idx = buf_next (first_idx);
+    }
 
-  /* A faithful emulation is going to have to be saved for a rainy day.  */
-  for (i = 0; i < nelem; i++)
+  for (elem = 0; elem < nelem; elem++)
     {
-      loadavg[i] = 0.0;
+      double avg = getavg (elem);
+
+      if (avg < 0)
+       break;
+      loadavg[elem] = avg;
     }
-  return i;
+
+  return elem;
 }
 
 /* Emulate getpwuid, getpwnam and others.  */
 
 #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 ()
+unsigned
+getuid (void)
 {
-  return the_passwd.pw_uid;
+  return dflt_passwd.pw_uid;
 }
 
-int
-geteuid ()
+unsigned
+geteuid (void)
 {
   /* I could imagine arguing for checking to see whether the user is
      in the Administrators group and returning a UID of 0 for that
@@ -550,30 +1037,30 @@ geteuid ()
   return getuid ();
 }
 
-int
-getgid ()
+unsigned
+getgid (void)
 {
-  return the_passwd.pw_gid;
+  return dflt_passwd.pw_gid;
 }
 
-int
-getegid ()
+unsigned
+getegid (void)
 {
   return getgid ();
 }
 
 struct passwd *
-getpwuid (int uid)
+getpwuid (unsigned 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 *
@@ -585,14 +1072,14 @@ getpwnam (char *name)
   if (!pw)
     return pw;
 
-  if (stricmp (name, pw->pw_name))
+  if (xstrcasecmp (name, pw->pw_name))
     return NULL;
 
   return pw;
 }
 
 void
-init_user_info ()
+init_user_info (void)
 {
   /* Find the user's real name by opening the process token and
      looking up the name associated with the user-sid in that token.
@@ -601,82 +1088,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         name[UNLEN+1], domain[1025];
-  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;
-  unsigned char buf[1024];
+  unsigned char *buf = NULL;
+  DWORD        blen = 0;
   TOKEN_USER   user_token;
   TOKEN_PRIMARY_GROUP group_token;
+  BOOL         result;
 
-  if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
-      && get_token_information (token, TokenUser,
-                               (PVOID)buf, sizeof (buf), &trash)
-      && (memcpy (&user_token, buf, sizeof (user_token)),
-         lookup_account_sid (NULL, user_token.User.Sid, name, &length,
-                             domain, &dlength, &user_type)))
+  result = open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token);
+  if (result)
     {
-      strcpy (the_passwd.pw_name, name);
+      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 (dflt_passwd.pw_name, uname);
       /* Determine a reasonable uid value.  */
-      if (stricmp ("administrator", name) == 0)
+      if (xstrcasecmp ("administrator", uname) == 0)
        {
-         the_passwd.pw_uid = 500; /* well-known Administrator uid */
-         the_passwd.pw_gid = 513; /* well-known None gid */
+         dflt_passwd.pw_uid = 500; /* well-known Administrator uid */
+         dflt_passwd.pw_gid = 513; /* well-known None gid */
        }
       else
        {
          /* Use the last sub-authority value of the RID, the relative
             portion of the SID, as user/group ID. */
-         DWORD n_subauthorities =
-           *get_sid_sub_authority_count (user_token.User.Sid);
+         dflt_passwd.pw_uid = get_rid (user_token.User.Sid);
 
-         if (n_subauthorities < 1)
-           the_passwd.pw_uid = 0;      /* the "World" RID */
-         else
+         /* Get group id and name.  */
+         result = get_token_information (token, TokenPrimaryGroup,
+                                         (LPVOID)buf, blen, &needed);
+         if (!result && GetLastError () == ERROR_INSUFFICIENT_BUFFER)
            {
-             the_passwd.pw_uid =
-               *get_sid_sub_authority (user_token.User.Sid,
-                                       n_subauthorities - 1);
+             buf = xrealloc (buf, blen = needed);
+             result = get_token_information (token, TokenPrimaryGroup,
+                                             (LPVOID)buf, blen, &needed);
            }
-
-         /* Get group id */
-         if (get_token_information (token, TokenPrimaryGroup,
-                                    (PVOID)buf, sizeof (buf), &trash))
+         if (result)
            {
              memcpy (&group_token, buf, sizeof (group_token));
-             n_subauthorities =
-               *get_sid_sub_authority_count (group_token.PrimaryGroup);
-
-             if (n_subauthorities < 1)
-               the_passwd.pw_gid = 0;  /* the "World" RID */
-             else
-               {
-                 the_passwd.pw_gid =
-                   *get_sid_sub_authority (group_token.PrimaryGroup,
-                                           n_subauthorities - 1);
-               }
+             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)
@@ -685,15 +1186,16 @@ 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);
 }
 
 int
-random ()
+random (void)
 {
   /* rand () on NT gives us 15 random bits...hack together 30 bits.  */
   return ((rand () << 15) | rand ());
@@ -711,9 +1213,7 @@ srandom (int seed)
    case path name components to lower case.  */
 
 static void
-normalize_filename (fp, path_sep)
-     register char *fp;
-     char path_sep;
+normalize_filename (register char *fp, char path_sep)
 {
   char sep;
   char *elem;
@@ -769,16 +1269,14 @@ normalize_filename (fp, path_sep)
 
 /* Destructively turn backslashes into slashes.  */
 void
-dostounix_filename (p)
-     register char *p;
+dostounix_filename (register char *p)
 {
   normalize_filename (p, '/');
 }
 
 /* Destructively turn slashes into backslashes.  */
 void
-unixtodos_filename (p)
-     register char *p;
+unixtodos_filename (register char *p)
 {
   normalize_filename (p, '\\');
 }
@@ -787,9 +1285,7 @@ unixtodos_filename (p)
    (From msdos.c...probably should figure out a way to share it,
    although this code isn't going to ever change.)  */
 int
-crlf_to_lf (n, buf)
-     register int n;
-     register unsigned char *buf;
+crlf_to_lf (register int n, register unsigned char *buf)
 {
   unsigned char *np = buf;
   unsigned char *startp = buf;
@@ -967,6 +1463,30 @@ sigunblock (int sig)
   return 0;
 }
 
+int
+sigemptyset (sigset_t *set)
+{
+  return 0;
+}
+
+int
+sigaddset (sigset_t *set, int signo)
+{
+  return 0;
+}
+
+int
+sigfillset (sigset_t *set)
+{
+  return 0;
+}
+
+int
+sigprocmask (int how, const sigset_t *set, sigset_t *oset)
+{
+  return 0;
+}
+
 int
 setpgrp (int pid, int gid)
 {
@@ -982,9 +1502,7 @@ alarm (int seconds)
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 LPBYTE
-w32_get_resource (key, lpdwtype)
-    char *key;
-    LPDWORD lpdwtype;
+w32_get_resource (char *key, LPDWORD lpdwtype)
 {
   LPBYTE lpvalue;
   HKEY hrootkey = NULL;
@@ -1005,7 +1523,7 @@ w32_get_resource (key, lpdwtype)
          return (lpvalue);
        }
 
-      if (lpvalue) xfree (lpvalue);
+      xfree (lpvalue);
 
       RegCloseKey (hrootkey);
     }
@@ -1022,7 +1540,7 @@ w32_get_resource (key, lpdwtype)
          return (lpvalue);
        }
 
-      if (lpvalue) xfree (lpvalue);
+      xfree (lpvalue);
 
       RegCloseKey (hrootkey);
     }
@@ -1104,7 +1622,7 @@ init_environment (char ** argv)
       {"LANG", NULL},
     };
 
-#define N_ENV_VARS sizeof(dflt_envvars)/sizeof(dflt_envvars[0])
+#define N_ENV_VARS sizeof (dflt_envvars)/sizeof (dflt_envvars[0])
 
     /* We need to copy dflt_envvars[] and work on the copy because we
        don't want the dumped Emacs to inherit the values of
@@ -1123,25 +1641,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.  */
@@ -1174,7 +1687,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];
 
@@ -1182,7 +1695,7 @@ init_environment (char ** argv)
          for (p = modname; *p; p++)
            if (*p == '\\') *p = '/';
 
-         _snprintf (buf, sizeof(buf)-1, "emacs_dir=%s", modname);
+         _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
          _putenv (strdup (buf));
        }
       /* Handle running emacs from the build directory: src/oo-spd/i386/  */
@@ -1190,7 +1703,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, '\\');
@@ -1198,7 +1711,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];
 
@@ -1206,7 +1719,7 @@ init_environment (char ** argv)
                  for (p = modname; *p; p++)
                    if (*p == '\\') *p = '/';
 
-                 _snprintf (buf, sizeof(buf)-1, "emacs_dir=%s", modname);
+                 _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
                  _putenv (strdup (buf));
                }
            }
@@ -1223,7 +1736,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;
@@ -1234,12 +1747,12 @@ init_environment (char ** argv)
                char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
 
                if (dwType == REG_EXPAND_SZ)
-                 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof(buf1));
+                 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof (buf1));
                else if (dwType == REG_SZ)
                  strcpy (buf1, lpval);
                if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
                  {
-                   _snprintf (buf2, sizeof(buf2)-1, "%s=%s", env_vars[i].name,
+                   _snprintf (buf2, sizeof (buf2)-1, "%s=%s", env_vars[i].name,
                               buf1);
                    _putenv (strdup (buf2));
                  }
@@ -1412,7 +1925,25 @@ get_emacs_configuration (void)
 char *
 get_emacs_configuration_options (void)
 {
-  static char options_buffer[256];
+  static char *options_buffer;
+  char cv[32];  /* Enough for COMPILER_VERSION.  */
+  char *options[] = {
+    cv,  /* To be filled later.  */
+#ifdef EMACSDEBUG
+    " --no-opt",
+#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
@@ -1425,18 +1956,18 @@ get_emacs_configuration_options (void)
 #endif
 #endif
 
-  sprintf (options_buffer, COMPILER_VERSION);
-#ifdef EMACSDEBUG
-  strcat (options_buffer, " --no-opt");
-#endif
-#ifdef USER_CFLAGS
-  strcat (options_buffer, " --cflags");
-  strcat (options_buffer, USER_CFLAGS);
-#endif
-#ifdef USER_LDFLAGS
-  strcat (options_buffer, " --ldflags");
-  strcat (options_buffer, USER_LDFLAGS);
-#endif
+  if (_snprintf (cv, sizeof (cv), COMPILER_VERSION) < 0)
+    return "Error: not enough space for compiler version";
+
+  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;
 }
 
@@ -1530,7 +2061,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;
 }
@@ -1582,7 +2113,7 @@ GetCachedVolumeInformation (char * root_dir)
      involve network access, and so is extremely quick).  */
 
   /* Map drive letter to UNC if remote. */
-  if ( isalpha( root_dir[0] ) && !fixed[ DRIVE_INDEX( root_dir[0] ) ] )
+  if (isalpha (root_dir[0]) && !fixed[DRIVE_INDEX (root_dir[0])])
     {
       char remote_name[ 256 ];
       char drive[3] = { root_dir[0], ':' };
@@ -1808,10 +2339,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,
@@ -1981,8 +2512,8 @@ open_unc_volume (const char *path)
   nr.lpComment = NULL;
   nr.lpProvider = NULL;
 
-  result = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
-                       RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+  result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+                        RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
 
   if (result == NO_ERROR)
     return henum;
@@ -2047,12 +2578,23 @@ logon_network_drive (const char *path)
   char share[MAX_PATH];
   int i, n_slashes;
   char drive[4];
+  UINT drvtype;
 
-  sprintf (drive, "%c:\\", path[0]);
+  if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
+    drvtype = DRIVE_REMOTE;
+  else if (path[0] == '\0' || path[1] != ':')
+    drvtype = GetDriveType (NULL);
+  else
+    {
+      drive[0] = path[0];
+      drive[1] = ':';
+      drive[2] = '\\';
+      drive[3] = '\0';
+      drvtype = GetDriveType (drive);
+    }
 
   /* Only logon to networked drives.  */
-  if ((!IS_DIRECTORY_SEP (path[0]) || !IS_DIRECTORY_SEP (path[1]))
-      && GetDriveType (drive) != DRIVE_REMOTE)
+  if (drvtype != DRIVE_REMOTE)
     return;
 
   n_slashes = 2;
@@ -2147,7 +2689,7 @@ sys_creat (const char * path, int mode)
 }
 
 FILE *
-sys_fopen(const char * path, const char * mode)
+sys_fopen (const char * path, const char * mode)
 {
   int fd;
   int oflag;
@@ -2234,7 +2776,7 @@ sys_link (const char * old, const char * new)
 
          data.wid.dwStreamId = BACKUP_LINK;
          data.wid.dwStreamAttributes = 0;
-         data.wid.Size.LowPart = wlen * sizeof(WCHAR);
+         data.wid.Size.LowPart = wlen * sizeof (WCHAR);
          data.wid.Size.HighPart = 0;
          data.wid.dwStreamNameSize = 0;
 
@@ -2384,7 +2926,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
@@ -2424,70 +2966,69 @@ sys_unlink (const char * path)
 }
 
 static FILETIME utc_base_ft;
-static long double utc_base;
+static ULONGLONG utc_base;  /* In 100ns units */
 static int init = 0;
 
+#define FILETIME_TO_U64(result, ft)        \
+  do {                                     \
+    ULARGE_INTEGER uiTemp;                 \
+    uiTemp.LowPart = (ft).dwLowDateTime;   \
+    uiTemp.HighPart = (ft).dwHighDateTime; \
+    result = uiTemp.QuadPart;              \
+  } while (0)
+
+static void
+initialize_utc_base (void)
+{
+  /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
+  SYSTEMTIME st;
+
+  st.wYear = 1970;
+  st.wMonth = 1;
+  st.wDay = 1;
+  st.wHour = 0;
+  st.wMinute = 0;
+  st.wSecond = 0;
+  st.wMilliseconds = 0;
+
+  SystemTimeToFileTime (&st, &utc_base_ft);
+  FILETIME_TO_U64 (utc_base, utc_base_ft);
+}
+
 static time_t
 convert_time (FILETIME ft)
 {
-  long double ret;
+  ULONGLONG tmp;
 
   if (!init)
     {
-      /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
-      SYSTEMTIME st;
-
-      st.wYear = 1970;
-      st.wMonth = 1;
-      st.wDay = 1;
-      st.wHour = 0;
-      st.wMinute = 0;
-      st.wSecond = 0;
-      st.wMilliseconds = 0;
-
-      SystemTimeToFileTime (&st, &utc_base_ft);
-      utc_base = (long double) utc_base_ft.dwHighDateTime
-       * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
+      initialize_utc_base();
       init = 1;
     }
 
   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);
+  FILETIME_TO_U64 (tmp, ft);
+  return (time_t) ((tmp - utc_base) / 10000000L);
 }
 
+
 void
 convert_from_time_t (time_t time, FILETIME * pft)
 {
-  long double tmp;
+  ULARGE_INTEGER tmp;
 
   if (!init)
     {
-      /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
-      SYSTEMTIME st;
-
-      st.wYear = 1970;
-      st.wMonth = 1;
-      st.wDay = 1;
-      st.wHour = 0;
-      st.wMinute = 0;
-      st.wSecond = 0;
-      st.wMilliseconds = 0;
-
-      SystemTimeToFileTime (&st, &utc_base_ft);
-      utc_base = (long double) utc_base_ft.dwHighDateTime
-       * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
+      initialize_utc_base ();
       init = 1;
     }
 
   /* time in 100ns units since 1-Jan-1601 */
-  tmp = (long double) time * 1e7 + utc_base;
-  pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024));
-  pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * pft->dwHighDateTime);
+  tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
+  pft->dwHighDateTime = tmp.HighPart;
+  pft->dwLowDateTime = tmp.LowPart;
 }
 
 #if 0
@@ -2533,19 +3074,238 @@ generate_inode_val (const char * name)
 
 #endif
 
-/* 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. */
-int
-stat (const char * path, struct stat * buf)
+static PSECURITY_DESCRIPTOR
+get_file_security_desc (const char *fname)
 {
-  char *name, *r;
-  WIN32_FIND_DATA wfd;
+  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 {
+  unsigned 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, unsigned *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, unsigned 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,
+                unsigned *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);
+    }
+}
+
+/* Return non-zero if NAME is a potentially slow filesystem.  */
+int
+is_slow_fs (const char *name)
+{
+  char drive_root[4];
+  UINT devtype;
+
+  if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
+    devtype = DRIVE_REMOTE;       /* assume UNC name is remote */
+  else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
+    devtype = GetDriveType (NULL); /* use root of current drive */
+  else
+    {
+      /* GetDriveType needs the root directory of the drive.  */
+      strncpy (drive_root, name, 2);
+      drive_root[2] = '\\';
+      drive_root[3] = '\0';
+      devtype = GetDriveType (drive_root);
+    }
+  return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
+}
+
+/* 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. */
+int
+stat (const char * path, struct stat * buf)
+{
+  char *name, *r;
+  char drive_root[4];
+  UINT devtype;
+  WIN32_FIND_DATA wfd;
   HANDLE fh;
   unsigned __int64 fake_inode;
   int permission;
   int len;
   int rootdir = FALSE;
+  PSECURITY_DESCRIPTOR psd = NULL;
 
   if (path == NULL || buf == NULL)
     {
@@ -2624,7 +3384,7 @@ 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;
@@ -2643,7 +3403,8 @@ stat (const char * path, struct stat * buf)
        }
     }
 
-  if (!NILP (Vw32_get_true_file_attributes)
+  if (!(NILP (Vw32_get_true_file_attributes)
+       || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
       /* No access rights required to get info.  */
       && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS, NULL))
@@ -2693,6 +3454,8 @@ stat (const char * path, struct stat * buf)
            }
        }
       CloseHandle (fh);
+      psd = get_file_security_desc (name);
+      get_file_owner_and_group (psd, name, buf);
     }
   else
     {
@@ -2701,7 +3464,10 @@ stat (const char * path, struct stat * buf)
        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.  */
@@ -2721,16 +3487,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);
@@ -2809,14 +3573,20 @@ fstat (int desc, 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;
+  /* 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);
@@ -2838,10 +3608,10 @@ fstat (int desc, struct stat * buf)
 #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))
+         (xstrcasecmp (p, ".exe") == 0 ||
+          xstrcasecmp (p, ".com") == 0 ||
+          xstrcasecmp (p, ".bat") == 0 ||
+          xstrcasecmp (p, ".cmd") == 0))
        permission |= S_IEXEC;
 #endif
     }
@@ -2888,8 +3658,706 @@ utime (const char *name, struct utimbuf *times)
   return 0;
 }
 
-#ifdef HAVE_SOCKETS
+\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
+list_system_processes (void)
+{
+  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 (long time_sec, long time_usec)
+{
+  return list3 (make_number ((time_sec >> 16) & 0xffff),
+               make_number (time_sec & 0xffff),
+               make_number (time_usec));
+}
+
+#define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L)
+
+static int
+process_times (HANDLE h_proc, Lisp_Object *ctime, Lisp_Object *etime,
+              Lisp_Object *stime, Lisp_Object *utime, Lisp_Object *ttime,
+              double *pcpu)
+{
+  FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
+  ULONGLONG tem1, tem2, tem3, 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);
+
+  FILETIME_TO_U64 (tem1, ft_kernel);
+  tem1 /= 10L;
+  *stime = U64_TO_LISP_TIME (tem1);
+
+  FILETIME_TO_U64 (tem2, ft_user);
+  tem2 /= 10L;
+  *utime = U64_TO_LISP_TIME (tem2);
+
+  tem3 = tem1 + tem2;
+  *ttime = U64_TO_LISP_TIME (tem3);
+
+  FILETIME_TO_U64 (tem, ft_creation);
+  /* Process no 4 (System) returns zero creation time.  */
+  if (tem)
+    tem = (tem - utc_base) / 10L;
+  *ctime = U64_TO_LISP_TIME (tem);
+
+  if (tem)
+    {
+      FILETIME_TO_U64 (tem3, ft_current);
+      tem = (tem3 - utc_base) / 10L - tem;
+    }
+  *etime = U64_TO_LISP_TIME (tem);
+
+  if (tem)
+    {
+      *pcpu = 100.0 * (tem1 + tem2) / tem;
+      if (*pcpu > 100)
+       *pcpu = 100.0;
+    }
+  else
+    *pcpu = 0;
+
+  return 1;
+}
+
+Lisp_Object
+system_process_attributes (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;
+  unsigned euid;
+  unsigned 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, ttime;
+  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);
+               }
+           }
+       }
+      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, &ttime, &pcpu))
+    {
+      attrs = Fcons (Fcons (Qutime, utime), attrs);
+      attrs = Fcons (Fcons (Qstime, stime), attrs);
+      attrs = Fcons (Fcons (Qtime,  ttime), 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
 /* Wrappers for  winsock functions to map between our file descriptors
    and winsock's handles; also set h_errno for convenience.
 
@@ -2984,34 +4452,34 @@ init_winsock (int load_now)
       if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \
         goto fail;
 
-      LOAD_PROC( WSAStartup );
-      LOAD_PROC( WSASetLastError );
-      LOAD_PROC( WSAGetLastError );
-      LOAD_PROC( WSAEventSelect );
-      LOAD_PROC( WSACreateEvent );
-      LOAD_PROC( WSACloseEvent );
-      LOAD_PROC( socket );
-      LOAD_PROC( bind );
-      LOAD_PROC( connect );
-      LOAD_PROC( ioctlsocket );
-      LOAD_PROC( recv );
-      LOAD_PROC( send );
-      LOAD_PROC( closesocket );
-      LOAD_PROC( shutdown );
-      LOAD_PROC( htons );
-      LOAD_PROC( ntohs );
-      LOAD_PROC( inet_addr );
-      LOAD_PROC( gethostname );
-      LOAD_PROC( gethostbyname );
-      LOAD_PROC( getservbyname );
-      LOAD_PROC( getpeername );
-      LOAD_PROC( WSACleanup );
-      LOAD_PROC( setsockopt );
-      LOAD_PROC( listen );
-      LOAD_PROC( getsockname );
-      LOAD_PROC( accept );
-      LOAD_PROC( recvfrom );
-      LOAD_PROC( sendto );
+      LOAD_PROC (WSAStartup);
+      LOAD_PROC (WSASetLastError);
+      LOAD_PROC (WSAGetLastError);
+      LOAD_PROC (WSAEventSelect);
+      LOAD_PROC (WSACreateEvent);
+      LOAD_PROC (WSACloseEvent);
+      LOAD_PROC (socket);
+      LOAD_PROC (bind);
+      LOAD_PROC (connect);
+      LOAD_PROC (ioctlsocket);
+      LOAD_PROC (recv);
+      LOAD_PROC (send);
+      LOAD_PROC (closesocket);
+      LOAD_PROC (shutdown);
+      LOAD_PROC (htons);
+      LOAD_PROC (ntohs);
+      LOAD_PROC (inet_addr);
+      LOAD_PROC (gethostname);
+      LOAD_PROC (gethostbyname);
+      LOAD_PROC (getservbyname);
+      LOAD_PROC (getpeername);
+      LOAD_PROC (WSACleanup);
+      LOAD_PROC (setsockopt);
+      LOAD_PROC (listen);
+      LOAD_PROC (getsockname);
+      LOAD_PROC (accept);
+      LOAD_PROC (recvfrom);
+      LOAD_PROC (sendto);
 #undef LOAD_PROC
 
       /* specify version 1.1 of winsock */
@@ -3047,11 +4515,11 @@ 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
-set_errno ()
+set_errno (void)
 {
   if (winsock_lib == NULL)
     h_errno = EINVAL;
@@ -3073,7 +4541,7 @@ set_errno ()
 }
 
 static void
-check_errno ()
+check_errno (void)
 {
   if (h_errno == 0 && winsock_lib != NULL)
     pfn_WSASetLastError (0);
@@ -3156,7 +4624,7 @@ struct {
 };
 
 char *
-sys_strerror(int error_no)
+sys_strerror (int error_no)
 {
   int i;
   static char unknown_msg[40];
@@ -3168,7 +4636,7 @@ sys_strerror(int error_no)
     if (_wsa_errlist[i].errnum == error_no)
       return _wsa_errlist[i].msg;
 
-  sprintf(unknown_msg, "Unidentified error: %d", error_no);
+  sprintf (unknown_msg, "Unidentified error: %d", error_no);
   return unknown_msg;
 }
 
@@ -3176,18 +4644,12 @@ sys_strerror(int error_no)
    but I believe the method of keeping the socket handle separate (and
    insuring it is not inheritable) is the correct one. */
 
-//#define SOCK_REPLACE_HANDLE
-
-#ifdef SOCK_REPLACE_HANDLE
-#define SOCK_HANDLE(fd) ((SOCKET) _get_osfhandle (fd))
-#else
 #define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd)
-#endif
 
 int socket_to_fd (SOCKET s);
 
 int
-sys_socket(int af, int type, int protocol)
+sys_socket (int af, int type, int protocol)
 {
   SOCKET s;
 
@@ -3226,13 +4688,6 @@ socket_to_fd (SOCKET s)
   fd = _open ("NUL:", _O_RDWR);
   if (fd >= 0)
     {
-#ifdef SOCK_REPLACE_HANDLE
-      /* now replace handle to NUL with our socket handle */
-      CloseHandle ((HANDLE) _get_osfhandle (fd));
-      _free_osfhnd (fd);
-      _set_osfhnd (fd, s);
-      /* setmode (fd, _O_BINARY); */
-#else
       /* Make a non-inheritable copy of the socket handle.  Note
         that it is possible that sockets aren't actually kernel
         handles, which appears to be the case on Windows 9x when
@@ -3278,7 +4733,6 @@ socket_to_fd (SOCKET s)
          }
       }
       fd_info[fd].hnd = (HANDLE) s;
-#endif
 
       /* set our own internal flags */
       fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
@@ -3390,7 +4844,7 @@ sys_gethostname (char * name, int namelen)
 }
 
 struct hostent *
-sys_gethostbyname(const char * name)
+sys_gethostbyname (const char * name)
 {
   struct hostent * host;
 
@@ -3408,7 +4862,7 @@ sys_gethostbyname(const char * name)
 }
 
 struct servent *
-sys_getservbyname(const char * name, const char * proto)
+sys_getservbyname (const char * name, const char * proto)
 {
   struct servent * serv;
 
@@ -3563,7 +5017,7 @@ sys_accept (int s, struct sockaddr * addr, int * addrlen)
 
 int
 sys_recvfrom (int s, char * buf, int len, int flags,
-         struct sockaddr * from, int * fromlen)
+             struct sockaddr * from, int * fromlen)
 {
   if (winsock_lib == NULL)
     {
@@ -3639,8 +5093,6 @@ fcntl (int s, int cmd, int options)
   return SOCKET_ERROR;
 }
 
-#endif /* HAVE_SOCKETS */
-
 
 /* Shadow main io functions: we need to handle pipes and sockets more
    intelligently, and implement non-blocking mode as well. */
@@ -3675,18 +5127,15 @@ sys_close (int fd)
            }
          if (i == MAXDESC)
            {
-#ifdef HAVE_SOCKETS
              if (fd_info[fd].flags & FILE_SOCKET)
                {
-#ifndef SOCK_REPLACE_HANDLE
                  if (winsock_lib == NULL) abort ();
 
                  pfn_shutdown (SOCK_HANDLE (fd), 2);
                  rc = pfn_closesocket (SOCK_HANDLE (fd));
-#endif
+
                  winsock_inuse--; /* count open sockets */
                }
-#endif
              delete_child (cp);
            }
        }
@@ -3798,10 +5247,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 ();
     }
 
@@ -3815,7 +5264,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)
@@ -3831,7 +5280,29 @@ _sys_read_ahead (int fd)
              Sleep (0);
        }
     }
-#ifdef HAVE_SOCKETS
+  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;
+       }
+    }
   else if (fd_info[fd].flags & FILE_SOCKET)
     {
       unsigned long nblock = 0;
@@ -3847,7 +5318,6 @@ _sys_read_ahead (int fd)
          pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
        }
     }
-#endif
 
   if (rc == sizeof (char))
     cp->status = STATUS_READ_SUCCEEDED;
@@ -3902,7 +5372,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;
 
@@ -3973,7 +5443,52 @@ sys_read (int fd, char * buffer, unsigned int count)
              if (to_read > 0)
                nchars += _read (fd, buffer, to_read);
            }
-#ifdef HAVE_SOCKETS
+         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;
+               }
+           }
          else /* FILE_SOCKET */
            {
              if (winsock_lib == NULL) abort ();
@@ -3992,15 +5507,14 @@ sys_read (int fd, char * buffer, unsigned int count)
                  int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0);
                  if (res == SOCKET_ERROR)
                    {
-                     DebPrint(("sys_read.recv failed with error %d on socket %ld\n",
-                               pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
+                     DebPrint (("sys_read.recv failed with error %d on socket %ld\n",
+                                pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
                      set_errno ();
                      return -1;
                    }
                  nchars += res;
                }
            }
-#endif
        }
       else
        {
@@ -4034,6 +5548,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)
@@ -4046,7 +5563,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)
        {
@@ -4087,8 +5604,42 @@ sys_write (int fd, const void * buffer, unsigned int count)
        }
     }
 
-#ifdef HAVE_SOCKETS
-  if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
+  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 if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
     {
       unsigned long nblock = 0;
       if (winsock_lib == NULL) abort ();
@@ -4110,20 +5661,46 @@ sys_write (int fd, const void * buffer, unsigned int count)
 
       if (nchars == SOCKET_ERROR)
         {
-         DebPrint(("sys_write.send failed with error %d on socket %ld\n",
-                   pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
+         DebPrint (("sys_write.send failed with error %d on socket %ld\n",
+                    pfn_WSAGetLastError (), SOCK_HANDLE (fd)));
          set_errno ();
        }
     }
   else
-#endif
-    nchars = _write (fd, buffer, count);
+    {
+      /* Some networked filesystems don't like too large writes, so
+        break them into smaller chunks.  See the Comments section of
+        the MSDN documentation of WriteFile for details behind the
+        choice of the value of CHUNK below.  See also the thread
+        http://thread.gmane.org/gmane.comp.version-control.git/145294
+        in the git mailing list.  */
+      const unsigned char *p = buffer;
+      const unsigned chunk = 30 * 1024 * 1024;
+
+      nchars = 0;
+      while (count > 0)
+       {
+         unsigned this_chunk = count < chunk ? count : chunk;
+         int n = _write (fd, p, this_chunk);
+
+         nchars += n;
+         if (n < 0)
+           {
+             nchars = n;
+             break;
+           }
+         else if (n < this_chunk)
+           break;
+         count -= n;
+         p += n;
+       }
+    }
 
   return nchars;
 }
 
 static void
-check_windows_init_file ()
+check_windows_init_file (void)
 {
   extern int noninteractive, inhibit_window_system;
 
@@ -4179,20 +5756,17 @@ check_windows_init_file ()
 }
 
 void
-term_ntproc ()
+term_ntproc (void)
 {
-#ifdef HAVE_SOCKETS
   /* shutdown the socket interface if necessary */
   term_winsock ();
-#endif
 
   term_w32select ();
 }
 
 void
-init_ntproc ()
+init_ntproc (void)
 {
-#ifdef HAVE_SOCKETS
   /* Initialise the socket interface now if available and requested by
      the user by defining PRELOAD_WINSOCK; otherwise loading will be
      delayed until open-network-stream is called (w32-has-winsock can
@@ -4206,7 +5780,6 @@ init_ntproc ()
 
   if (getenv ("PRELOAD_WINSOCK") != NULL)
     init_winsock (TRUE);
-#endif
 
   /* Initial preparation for subprocess support: replace our standard
      handles with non-inheritable versions. */
@@ -4298,7 +5871,8 @@ init_ntproc ()
         shutdown_handler ensures that buffers' autosave files are
        up to date when the user logs off, or the system shuts down.
 */
-BOOL WINAPI shutdown_handler(DWORD type)
+BOOL WINAPI
+shutdown_handler (DWORD type)
 {
   /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them.  */
   if (type == CTRL_CLOSE_EVENT        /* User closes console window.  */
@@ -4319,7 +5893,7 @@ BOOL WINAPI shutdown_handler(DWORD type)
        initialized is non zero (see the function main in emacs.c).
 */
 void
-globals_of_w32 ()
+globals_of_w32 (void)
 {
   HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
 
@@ -4333,11 +5907,225 @@ globals_of_w32 ()
   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;
+  g_b_init_get_native_system_info = 0;
+  g_b_init_get_system_times = 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
      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);
+  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 */