Upgraded to mh-e version 6.1.1.
[bpt/emacs.git] / src / w32.c
index 6008afe..7a31e78 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,5 +1,5 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
-   Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1994, 1995, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -30,10 +30,16 @@ Boston, MA 02111-1307, USA.
 #include <fcntl.h>
 #include <ctype.h>
 #include <signal.h>
+#include <sys/file.h>
 #include <sys/time.h>
+#include <sys/utime.h>
 
 /* must include CRT headers *before* config.h */
-#include "config.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #undef access
 #undef chdir
 #undef chmod
@@ -55,10 +61,16 @@ Boston, MA 02111-1307, USA.
 #undef read
 #undef write
 
+#undef strerror
+
 #include "lisp.h"
 
 #include <pwd.h>
 
+#ifdef __GNUC__
+#define _ANONYMOUS_UNION
+#define _ANONYMOUS_STRUCT
+#endif
 #include <windows.h>
 
 #ifdef HAVE_SOCKETS    /* TCP connection support, if kernel can do it */
@@ -72,21 +84,44 @@ Boston, MA 02111-1307, USA.
 #undef gethostname
 #undef gethostbyname
 #undef getservbyname
+#undef getpeername
 #undef shutdown
+#undef setsockopt
+#undef listen
+#undef getsockname
+#undef accept
+#undef recvfrom
+#undef sendto
 #endif
 
 #include "w32.h"
 #include "ndir.h"
 #include "w32heap.h"
-#undef min
-#undef max
-#define min(x, y) (((x) < (y)) ? (x) : (y))
-#define max(x, y) (((x) > (y)) ? (x) : (y))
+#include "systime.h"
 
 extern Lisp_Object Vw32_downcase_file_names;
 extern Lisp_Object Vw32_generate_fake_inodes;
 extern Lisp_Object Vw32_get_true_file_attributes;
+extern Lisp_Object Vw32_num_mouse_buttons;
+
+\f
+/* Equivalent of strerror for W32 error codes.  */
+char *
+w32_strerror (int error_no)
+{
+  static char buf[500];
+
+  if (error_no == 0)
+    error_no = GetLastError ();
+
+  buf[0] = '\0';
+  if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
+                     error_no,
+                     0, /* choose most suitable language */
+                     buf, sizeof (buf), NULL))
+    sprintf (buf, "w32 error %u", error_no);
+  return buf;
+}
 
 static char startup_dir[MAXPATHLEN];
 
@@ -284,9 +319,9 @@ init_user_info ()
 
   /* Ensure HOME and SHELL are defined. */
   if (getenv ("HOME") == NULL)
-    putenv ("HOME=c:/");
+    abort ();
   if (getenv ("SHELL") == NULL)
-    putenv (os_subtype == OS_WIN95 ? "SHELL=command" : "SHELL=cmd");
+    abort ();
 
   /* Set dir and shell from environment variables. */
   strcpy (the_passwd.pw_dir, getenv ("HOME"));
@@ -463,6 +498,10 @@ get_long_basename (char * name, char * buf, int size)
   HANDLE dir_handle;
   int len = 0;
 
+  /* must be valid filename, no wild cards or other invalid characters */
+  if (strpbrk (name, "*?|<>\""))
+    return 0;
+
   dir_handle = FindFirstFile (name, &find_data);
   if (dir_handle != INVALID_HANDLE_VALUE)
     {
@@ -497,9 +536,10 @@ w32_get_long_filename (char * name, char * buf, int size)
   len = parse_root (full, &p);
   memcpy (o, full, len);
   o += len;
+  *o = '\0';
   size -= len;
 
-  do
+  while (p != NULL && *p)
     {
       q = p;
       p = strchr (q, '\\');
@@ -522,11 +562,23 @@ w32_get_long_filename (char * name, char * buf, int size)
       else
        return FALSE;
     }
-  while (p != NULL && *p);
 
   return TRUE;
 }
 
+int
+is_unc_volume (const char *filename)
+{
+  const char *ptr = filename;
+
+  if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
+    return 0;
+
+  if (strpbrk (ptr + 2, "*?|<>\"\\/"))
+    return 0;
+
+  return 1;
+}
 
 /* Routines that are no-ops on NT but are defined to get Emacs to compile.  */
 
@@ -536,12 +588,24 @@ sigsetmask (int signal_mask)
   return 0;
 }
 
+int 
+sigmask (int sig) 
+{ 
+  return 0;
+}
+
 int 
 sigblock (int sig) 
 { 
   return 0;
 }
 
+int 
+sigunblock (int sig) 
+{ 
+  return 0;
+}
+
 int 
 setpgrp (int pid, int gid) 
 { 
@@ -620,54 +684,149 @@ char *get_emacs_configuration (void);
 extern Lisp_Object Vsystem_configuration;
 
 void
-init_environment ()
+init_environment (char ** argv)
 {
-  /* Check for environment variables and use registry if they don't exist */
+  static const char * const tempdirs[] = {
+    "$TMPDIR", "$TEMP", "$TMP", "c:/"
+  };
+  int i;
+  const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]);
+
+  /* Make sure they have a usable $TMPDIR.  Many Emacs functions use
+     temporary files and assume "/tmp" if $TMPDIR is unset, which
+     will break on DOS/Windows.  Refuse to work if we cannot find
+     a directory, not even "c:/", usable for that purpose.  */
+  for (i = 0; i < imax ; i++)
+    {
+      const char *tmp = tempdirs[i];
+
+      if (*tmp == '$')
+       tmp = getenv (tmp + 1);
+      /* Note that `access' can lie to us if the directory resides on a
+        read-only filesystem, like CD-ROM or a write-protected floppy.
+        The only way to be really sure is to actually create a file and
+        see if it succeeds.  But I think that's too much to ask.  */
+      if (tmp && _access (tmp, D_OK) == 0)
+       {
+         char * var = alloca (strlen (tmp) + 8);
+         sprintf (var, "TMPDIR=%s", tmp);
+         _putenv (strdup (var));
+         break;
+       }
+    }
+  if (i >= imax)
+    cmd_error_internal
+      (Fcons (Qerror,
+             Fcons (build_string ("no usable temporary directories found!!"),
+                    Qnil)),
+       "While setting TMPDIR: ");
+
+  /* Check for environment variables and use registry settings if they
+     don't exist.  Fallback on default values where applicable.  */
   {
     int i;
     LPBYTE lpval;
     DWORD dwType;
+    char locale_name[32];
 
-    static char * env_vars[] = 
-    {
-      "HOME",
-      "PRELOAD_WINSOCK",
-      "emacs_dir",
-      "EMACSLOADPATH",
-      "SHELL",
-      "CMDPROXY",
-      "EMACSDATA",
-      "EMACSPATH",
-      "EMACSLOCKDIR",
+    static struct env_entry
+    {
+      char * name;
+      char * def_value;
+    } env_vars[] = 
+    {
+      {"HOME", "C:/"},
+      {"PRELOAD_WINSOCK", NULL},
+      {"emacs_dir", "C:/emacs"},
+      {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"},
+      {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
+      {"EMACSDATA", "%emacs_dir%/etc"},
+      {"EMACSPATH", "%emacs_dir%/bin"},
       /* We no longer set INFOPATH because Info-default-directory-list
-        is then ignored.  We use a hook in winnt.el instead.  */
-      /*      "INFOPATH", */
-      "EMACSDOC",
-      "TERM",
+        is then ignored.  */
+      /*  {"INFOPATH", "%emacs_dir%/info"},  */
+      {"EMACSDOC", "%emacs_dir%/etc"},
+      {"TERM", "cmd"},
+      {"LANG", NULL},
     };
 
-    for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) 
+  /* Get default locale info and use it for LANG.  */
+  if (GetLocaleInfo (LOCALE_USER_DEFAULT,
+                     LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
+                     locale_name, sizeof (locale_name)))
+    {
+      for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
+        {
+          if (strcmp (env_vars[i].name, "LANG") == 0)
+            {
+              env_vars[i].def_value = locale_name;
+              break;
+            }
+        }
+    }
+
+#define SET_ENV_BUF_SIZE (4 * MAX_PATH)        /* to cover EMACSLOADPATH */
+
+    /* Treat emacs_dir specially: set it unconditionally based on our
+       location, if it appears that we are running from the bin subdir
+       of a standard installation.  */
+    {
+      char *p;
+      char modname[MAX_PATH];
+
+      if (!GetModuleFileName (NULL, modname, MAX_PATH))
+       abort ();
+      if ((p = strrchr (modname, '\\')) == NULL)
+       abort ();
+      *p = 0;
+
+      if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0)
+       {
+         char buf[SET_ENV_BUF_SIZE];
+
+         *p = 0;
+         for (p = modname; *p; p++)
+           if (*p == '\\') *p = '/';
+                 
+         _snprintf (buf, sizeof(buf)-1, "emacs_dir=%s", modname);
+         _putenv (strdup (buf));
+       }
+    }
+
+    for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
       {
-       if (!getenv (env_vars[i])
-           && (lpval = w32_get_resource (env_vars[i], &dwType)) != NULL)
+       if (!getenv (env_vars[i].name))
          {
-           if (dwType == REG_EXPAND_SZ)
-             {
-               char buf1[500], buf2[500];
+           int dont_free = 0;
 
-               ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500);
-               _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1);
-               putenv (strdup (buf2));
+           if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL)
+             {
+               lpval = env_vars[i].def_value;
+               dwType = REG_EXPAND_SZ;
+               dont_free = 1;
              }
-           else if (dwType == REG_SZ)
+
+           if (lpval)
              {
-               char buf[500];
+               if (dwType == REG_EXPAND_SZ)
+                 {
+                   char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
+
+                   ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof(buf1));
+                   _snprintf (buf2, sizeof(buf2)-1, "%s=%s", env_vars[i].name, buf1);
+                   _putenv (strdup (buf2));
+                 }
+               else if (dwType == REG_SZ)
+                 {
+                   char buf[SET_ENV_BUF_SIZE];
                  
-               _snprintf (buf, 499, "%s=%s", env_vars[i], lpval);
-               putenv (strdup (buf));
-             }
+                   _snprintf (buf, sizeof(buf)-1, "%s=%s", env_vars[i].name, lpval);
+                   _putenv (strdup (buf));
+                 }
 
-           xfree (lpval);
+               if (!dont_free)
+                 xfree (lpval);
+             }
          }
       }
   }
@@ -703,7 +862,7 @@ init_environment ()
 
   {
     char *p;
-    char modname[MAX_PATH];
+    static char modname[MAX_PATH];
 
     if (!GetModuleFileName (NULL, modname, MAX_PATH))
       abort ();
@@ -712,22 +871,46 @@ init_environment ()
     *p = 0;
 
     SetCurrentDirectory (modname);
+
+    /* Ensure argv[0] has the full path to Emacs.  */
+    *p = '\\';
+    argv[0] = modname;
   }
 
+  /* Determine if there is a middle mouse button, to allow parse_button
+     to decide whether right mouse events should be mouse-2 or
+     mouse-3. */
+  XSETINT (Vw32_num_mouse_buttons, GetSystemMetrics (SM_CMOUSEBUTTONS));
+
   init_user_info ();
 }
 
+char *
+emacs_root_dir (void)
+{
+  static char root_dir[FILENAME_MAX];
+  const char *p;
+
+  p = getenv ("emacs_dir");
+  if (p == NULL)
+    abort ();
+  strcpy (root_dir, p);
+  root_dir[parse_root (root_dir, NULL)] = '\0';
+  dostounix_filename (root_dir);
+  return root_dir;
+}
+
 /* We don't have scripts to automatically determine the system configuration
    for Emacs before it's compiled, and we don't want to have to make the
    user enter it, so we define EMACS_CONFIGURATION to invoke this runtime
    routine.  */
 
-static char configuration_buffer[32];
-
 char *
 get_emacs_configuration (void)
 {
   char *arch, *oem, *os;
+  int build_num;
+  static char configuration_buffer[32];
 
   /* Determine the processor type.  */
   switch (get_processor_type ()) 
@@ -766,23 +949,91 @@ get_emacs_configuration (void)
       break;
     }
 
-  /* Let oem be "*" until we figure out how to decode the OEM field.  */
-  oem = "*";
+  /* Use the OEM field to reflect the compiler/library combination.  */
+#ifdef _MSC_VER
+#define COMPILER_NAME  "msvc"
+#else
+#ifdef __GNUC__
+#define COMPILER_NAME  "mingw"
+#else
+#define COMPILER_NAME  "unknown"
+#endif
+#endif
+  oem = COMPILER_NAME;
+
+  switch (osinfo_cache.dwPlatformId) {
+  case VER_PLATFORM_WIN32_NT:
+    os = "nt";
+    build_num = osinfo_cache.dwBuildNumber;
+    break;
+  case VER_PLATFORM_WIN32_WINDOWS:
+    if (osinfo_cache.dwMinorVersion == 0) {
+      os = "windows95";
+    } else {
+      os = "windows98";
+    }
+    build_num = LOWORD (osinfo_cache.dwBuildNumber);
+    break;
+  case VER_PLATFORM_WIN32s:
+    /* Not supported, should not happen. */
+    os = "windows32s";
+    build_num = LOWORD (osinfo_cache.dwBuildNumber);
+    break;
+  default:
+    os = "unknown";
+    build_num = 0;
+    break;
+  }
 
-  os = (GetVersion () & OS_WIN95) ? "windows95" : "nt";
+  if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
+            get_w32_major_version (), get_w32_minor_version (), build_num);
+  } else {
+    sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
+  }
 
-  sprintf (configuration_buffer, "%s-%s-%s%d.%d", arch, oem, os,
-          get_w32_major_version (), get_w32_minor_version ());
   return configuration_buffer;
 }
 
+char *
+get_emacs_configuration_options (void)
+{
+  static char options_buffer[256];
+
+/* Work out the effective configure options for this build.  */
+#ifdef _MSC_VER
+#define COMPILER_VERSION       "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100
+#else
+#ifdef __GNUC__
+#define COMPILER_VERSION       "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__
+#else
+#define COMPILER_VERSION       ""
+#endif
+#endif
+
+  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
+  return options_buffer;
+}
+
+
 #include <sys/timeb.h>
 
 /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95).  */
 void 
 gettimeofday (struct timeval *tv, struct timezone *tz)
 {
-  struct _timeb tb;
+  struct timeb tb;
   _ftime (&tb);
 
   tv->tv_sec = tb.time;
@@ -873,7 +1124,7 @@ lookup_volume_info (char * root_dir)
 static void
 add_volume_info (char * root_dir, volume_info_data * info)
 {
-  info->root_dir = strdup (root_dir);
+  info->root_dir = xstrdup (root_dir);
   info->next = volume_cache;
   volume_cache = info;
 }
@@ -956,15 +1207,15 @@ GetCachedVolumeInformation (char * root_dir)
       }
     else
       {
-       free (info->name);
-       free (info->type);
+       xfree (info->name);
+       xfree (info->type);
       }
 
-    info->name = strdup (name);
+    info->name = xstrdup (name);
     info->serialnum = serialnum;
     info->maxcomp = maxcomp;
     info->flags = flags;
-    info->type = strdup (type);
+    info->type = xstrdup (type);
     info->timestamp = GetTickCount ();
   }
 
@@ -1042,7 +1293,14 @@ map_w32_filename (const char * name, const char ** pPath)
   char * path;
   const char * save_name = name;
 
-  if (is_fat_volume (name, &path)) /* truncate to 8.3 */
+  if (strlen (name) >= MAX_PATH)
+    {
+      /* Return a filename which will cause callers to fail.  */
+      strcpy (shortname, "?");
+      return shortname;
+    }
+
+  if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */
     {
       register int left = 8;   /* maximum number of chars in part */
       register int extn = 0;   /* extension added? */
@@ -1130,6 +1388,18 @@ map_w32_filename (const char * name, const char ** pPath)
   return shortname;
 }
 
+static int
+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));
+}
+
 /* Emulate the Unix directory procedures opendir, closedir, 
    and readdir.  We can't use the procedures supplied in sysdep.c,
    so we provide them here.  */
@@ -1140,6 +1410,13 @@ static int    dir_is_fat;
 static char   dir_pathname[MAXPATHLEN+1];
 static WIN32_FIND_DATA dir_find_data;
 
+/* Support shares on a network resource as subdirectories of a read-only
+   root directory. */
+static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
+HANDLE open_unc_volume (char *);
+char  *read_unc_volume (HANDLE, char *, int);
+void   close_unc_volume (HANDLE);
+
 DIR *
 opendir (char *filename)
 {
@@ -1148,10 +1425,20 @@ opendir (char *filename)
   /* Opening is done by FindFirstFile.  However, a read is inherent to
      this operation, so we defer the open until read time.  */
 
-  if (!(dirp = (DIR *) malloc (sizeof (DIR))))
-    return NULL;
   if (dir_find_handle != INVALID_HANDLE_VALUE)
     return NULL;
+  if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+    return NULL;
+
+  if (is_unc_volume (filename))
+    {
+      wnet_enum_handle = open_unc_volume (filename);
+      if (wnet_enum_handle == INVALID_HANDLE_VALUE)
+       return NULL;
+    }
+
+  if (!(dirp = (DIR *) malloc (sizeof (DIR))))
+    return NULL;
 
   dirp->dd_fd = 0;
   dirp->dd_loc = 0;
@@ -1173,14 +1460,26 @@ closedir (DIR *dirp)
       FindClose (dir_find_handle);
       dir_find_handle = INVALID_HANDLE_VALUE;
     }
+  else if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+    {
+      close_unc_volume (wnet_enum_handle);
+      wnet_enum_handle = INVALID_HANDLE_VALUE;
+    }
   xfree ((char *) dirp);
 }
 
 struct direct *
 readdir (DIR *dirp)
 {
+  if (wnet_enum_handle != INVALID_HANDLE_VALUE)
+    {
+      if (!read_unc_volume (wnet_enum_handle, 
+                             dir_find_data.cFileName, 
+                             MAX_PATH))
+       return NULL;
+    }
   /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
-  if (dir_find_handle == INVALID_HANDLE_VALUE)
+  else if (dir_find_handle == INVALID_HANDLE_VALUE)
     {
       char filename[MAXNAMLEN + 3];
       int ln;
@@ -1226,6 +1525,80 @@ readdir (DIR *dirp)
   return &dir_static;
 }
 
+HANDLE
+open_unc_volume (char *path)
+{
+  NETRESOURCE nr; 
+  HANDLE henum;
+  int result;
+
+  nr.dwScope = RESOURCE_GLOBALNET; 
+  nr.dwType = RESOURCETYPE_DISK; 
+  nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; 
+  nr.dwUsage = RESOURCEUSAGE_CONTAINER; 
+  nr.lpLocalName = NULL; 
+  nr.lpRemoteName = map_w32_filename (path, NULL);
+  nr.lpComment = NULL; 
+  nr.lpProvider = NULL;   
+
+  result = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK,  
+                       RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+
+  if (result == NO_ERROR)
+    return henum;
+  else
+    return INVALID_HANDLE_VALUE;
+}
+
+char *
+read_unc_volume (HANDLE henum, char *readbuf, int size)
+{
+  DWORD count;
+  int result;
+  DWORD bufsize = 512;
+  char *buffer;
+  char *ptr;
+
+  count = 1;
+  buffer = alloca (bufsize);
+  result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize);
+  if (result != NO_ERROR)
+    return NULL;
+
+  /* WNetEnumResource returns \\resource\share...skip forward to "share". */
+  ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
+  ptr += 2;
+  while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+  ptr++;
+
+  strncpy (readbuf, ptr, size);
+  return readbuf;
+}
+
+void
+close_unc_volume (HANDLE henum)
+{
+  if (henum != INVALID_HANDLE_VALUE)
+    WNetCloseEnum (henum);
+}
+
+DWORD
+unc_volume_file_attributes (char *path)
+{
+  HANDLE henum;
+  DWORD attrs;
+
+  henum = open_unc_volume (path);
+  if (henum == INVALID_HANDLE_VALUE)
+    return -1;
+
+  attrs = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_DIRECTORY;
+
+  close_unc_volume (henum);
+
+  return attrs;
+}
+
 
 /* Shadow some MSVC runtime functions to map requests for long filenames
    to reasonable short names if necessary.  This was originally added to
@@ -1235,7 +1608,41 @@ readdir (DIR *dirp)
 int
 sys_access (const char * path, int mode)
 {
-  return _access (map_w32_filename (path, NULL), mode);
+  DWORD attributes;
+
+  /* MSVC implementation doesn't recognize D_OK.  */
+  path = map_w32_filename (path, NULL);
+  if (is_unc_volume (path))
+    {
+      attributes = unc_volume_file_attributes (path);
+      if (attributes == -1) {
+       errno = EACCES;
+       return -1;
+      }
+    }
+  else if ((attributes = GetFileAttributes (path)) == -1)
+    {
+      /* Should try mapping GetLastError to errno; for now just indicate
+        that path doesn't exist.  */
+      errno = EACCES;
+      return -1;
+    }
+  if ((mode & X_OK) != 0 && !is_exec (path))
+    {
+      errno = EACCES;
+      return -1;
+    }
+  if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0)
+    {
+      errno = EACCES;
+      return -1;
+    }
+  if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+    {
+      errno = EACCES;
+      return -1;
+    }
+  return 0;
 }
 
 int
@@ -1432,15 +1839,21 @@ sys_mktemp (char * template)
 int
 sys_open (const char * path, int oflag, int mode)
 {
-  /* Force all file handles to be non-inheritable. */
-  return _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, mode);
+  const char* mpath = map_w32_filename (path, NULL);
+  /* Try to open file without _O_CREAT, to be able to write to hidden
+     and system files. Force all file handles to be
+     non-inheritable. */
+  int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+  if (res >= 0)
+    return res;
+  return _open (mpath, oflag | _O_NOINHERIT, mode);
 }
 
 int
 sys_rename (const char * oldname, const char * newname)
 {
+  BOOL result;
   char temp[MAX_PATH];
-  DWORD attr;
 
   /* MoveFile on Windows 95 doesn't correctly change the short file name
      alias in a number of circumstances (it is not easy to predict when
@@ -1459,37 +1872,56 @@ sys_rename (const char * oldname, const char * newname)
 
   if (os_subtype == OS_WIN95)
     {
+      char * o;
       char * p;
+      int    i = 0;
+
+      oldname = map_w32_filename (oldname, NULL);
+      if (o = strrchr (oldname, '\\'))
+       o++;
+      else
+       o = (char *) oldname;
 
       if (p = strrchr (temp, '\\'))
        p++;
       else
        p = temp;
-      /* Force temp name to require a manufactured 8.3 alias - this
-        seems to make the second rename work properly. */
-      strcpy (p, "_rename_temp.XXXXXX");
-      sys_mktemp (temp);
-      if (rename (map_w32_filename (oldname, NULL), temp) < 0)
+
+      do
+       {
+         /* Force temp name to require a manufactured 8.3 alias - this
+            seems to make the second rename work properly.  */
+         sprintf (p, "_.%s.%u", o, i);
+         i++;
+         result = rename (oldname, temp);
+       }
+      /* This loop must surely terminate!  */
+      while (result < 0 && errno == EEXIST);
+      if (result < 0)
        return -1;
     }
 
   /* Emulate Unix behaviour - newname is deleted if it already exists
      (at least if it is a file; don't do this for directories).
-     However, don't do this if we are just changing the case of the file
-     name - we will end up deleting the file we are trying to rename!  */
+
+     Since we mustn't do this if we are just changing the case of the
+     file name (we would end up deleting the file we are trying to
+     rename!), we let rename detect if the destination file already
+     exists - that way we avoid the possible pitfalls of trying to
+     determine ourselves whether two names really refer to the same
+     file, which is not always possible in the general case.  (Consider
+     all the permutations of shared or subst'd drives, etc.)  */
+
   newname = map_w32_filename (newname, NULL);
+  result = rename (temp, newname);
 
-  /* TODO: Use GetInformationByHandle (on NT) to ensure newname and temp
-     do not refer to the same file, eg. through share aliases.  */
-  if (stricmp (newname, temp) != 0
-      && (attr = GetFileAttributes (newname)) != -1
-      && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
-    {
-      _chmod (newname, 0666);
-      _unlink (newname);
-    }
+  if (result < 0
+      && errno == EEXIST
+      && _chmod (newname, 0666) == 0
+      && _unlink (newname) == 0)
+    result = rename (temp, newname);
 
-  return rename (temp, newname);
+  return result;
 }
 
 int
@@ -1501,7 +1933,11 @@ sys_rmdir (const char * path)
 int
 sys_unlink (const char * path)
 {
-  return _unlink (map_w32_filename (path, NULL));
+  path = map_w32_filename (path, NULL);
+
+  /* On Unix, unlink works without write permission. */
+  _chmod (path, 0666);
+  return _unlink (path);
 }
 
 static FILETIME utc_base_ft;
@@ -1540,8 +1976,6 @@ convert_time (FILETIME ft)
   return (time_t) (ret * 1e-7);
 }
 
-#if 0
-/* in case we ever have need of this */
 void
 convert_from_time_t (time_t time, FILETIME * pft)
 {
@@ -1569,9 +2003,8 @@ convert_from_time_t (time_t time, FILETIME * pft)
   /* 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 - pft->dwHighDateTime);
+  pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * pft->dwHighDateTime);
 }
-#endif
 
 #if 0
 /* No reason to keep this; faking inode values either by hashing or even
@@ -1622,7 +2055,7 @@ generate_inode_val (const char * name)
 int
 stat (const char * path, struct stat * buf)
 {
-  char * name;
+  char *name, *r;
   WIN32_FIND_DATA wfd;
   HANDLE fh;
   DWORD fake_inode;
@@ -1637,13 +2070,20 @@ stat (const char * path, struct stat * buf)
     }
 
   name = (char *) map_w32_filename (path, &path);
-  /* must be valid filename, no wild cards */
-  if (strchr (name, '*') || strchr (name, '?'))
+  /* must be valid filename, no wild cards or other invalid characters */
+  if (strpbrk (name, "*?|<>\""))
     {
       errno = ENOENT;
       return -1;
     }
 
+  /* If name is "c:/.." or "/.." then stat "c:/" or "/".  */
+  r = IS_DEVICE_SEP (name[1]) ? &name[2] : name;
+  if (IS_DIRECTORY_SEP (r[0]) && r[1] == '.' && r[2] == '.' && r[3] == '\0')
+    {
+      r[1] = r[2] = '\0';
+    }
+
   /* Remove trailing directory separator, unless name is the root
      directory of a drive or UNC volume in which case ensure there
      is a trailing separator. */
@@ -1652,7 +2092,21 @@ stat (const char * path, struct stat * buf)
             && (IS_DIRECTORY_SEP (*path) || *path == 0));
   name = strcpy (alloca (len + 2), name);
 
-  if (rootdir)
+  if (is_unc_volume (name))
+    {
+      DWORD attrs = unc_volume_file_attributes (name);
+
+      if (attrs == -1)
+       return -1;
+
+      memset (&wfd, 0, sizeof (wfd));
+      wfd.dwFileAttributes = attrs;
+      wfd.ftCreationTime = utc_base_ft;
+      wfd.ftLastAccessTime = utc_base_ft;
+      wfd.ftLastWriteTime = utc_base_ft;
+      strcpy (wfd.cFileName, name);
+    }
+  else if (rootdir)
     {
       if (!IS_DIRECTORY_SEP (name[len-1]))
        strcat (name, "\\");
@@ -1676,9 +2130,11 @@ stat (const char * path, struct stat * buf)
       /* (This is hacky, but helps when doing file completions on
         network drives.)  Optimize by using information available from
         active readdir if possible.  */
+      len = strlen (dir_pathname);
+      if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+       len--;
       if (dir_find_handle != INVALID_HANDLE_VALUE
-         && (len = strlen (dir_pathname)),
-         strnicmp (name, dir_pathname, len) == 0
+         && strnicmp (name, dir_pathname, len) == 0
          && IS_DIRECTORY_SEP (name[len])
          && stricmp (name + len + 1, dir_static.d_name) == 0)
        {
@@ -1697,23 +2153,38 @@ stat (const char * path, struct stat * buf)
        }
     }
 
-  if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-    {
-      buf->st_mode = _S_IFDIR;
-      buf->st_nlink = 2;       /* doesn't really matter */
-      fake_inode = 0;          /* this doesn't either I think */
-    }
-  else if (!NILP (Vw32_get_true_file_attributes))
+  if (!NILP (Vw32_get_true_file_attributes)
+      /* No access rights required to get info.  */
+      && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
+                          FILE_FLAG_BACKUP_SEMANTICS, NULL))
+         != INVALID_HANDLE_VALUE)
     {
       /* This is more accurate in terms of gettting the correct number
         of links, but is quite slow (it is noticable when Emacs is
         making a list of file name completions). */
       BY_HANDLE_FILE_INFORMATION info;
 
-      /* No access rights required to get info.  */
-      fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
-
       if (GetFileInformationByHandle (fh, &info))
+       {
+         buf->st_nlink = info.nNumberOfLinks;
+         /* Might as well use file index to fake inode values, but this
+            is not guaranteed to be unique unless we keep a handle open
+            all the time (even then there are situations where it is
+            not unique).  Reputedly, there are at most 48 bits of info
+            (on NTFS, presumably less on FAT). */
+         fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+       }
+      else
+       {
+         buf->st_nlink = 1;
+         fake_inode = 0;
+       }
+
+      if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+       {
+         buf->st_mode = _S_IFDIR;
+       }
+      else
        {
          switch (GetFileType (fh))
            {
@@ -1728,25 +2199,14 @@ stat (const char * path, struct stat * buf)
            default:
              buf->st_mode = _S_IFCHR;
            }
-         buf->st_nlink = info.nNumberOfLinks;
-         /* Might as well use file index to fake inode values, but this
-            is not guaranteed to be unique unless we keep a handle open
-            all the time (even then there are situations where it is
-            not unique).  Reputedly, there are at most 48 bits of info
-            (on NTFS, presumably less on FAT). */
-         fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
-         CloseHandle (fh);
-       }
-      else
-       {
-         errno = EACCES;
-         return -1;
        }
+      CloseHandle (fh);
     }
   else
     {
       /* Don't bother to make this information more accurate.  */
-      buf->st_mode = _S_IFREG;
+      buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ?
+       _S_IFDIR : _S_IFREG;
       buf->st_nlink = 1;
       fake_inode = 0;
     }
@@ -1795,15 +2255,101 @@ stat (const char * path, struct stat * buf)
   
   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
     permission |= _S_IEXEC;
+  else if (is_exec (name))
+    permission |= _S_IEXEC;
+
+  buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
+
+  return 0;
+}
+
+/* Provide fstat and utime as well as stat for consistent handling of
+   file timestamps. */
+int
+fstat (int desc, struct stat * buf)
+{
+  HANDLE fh = (HANDLE) _get_osfhandle (desc);
+  BY_HANDLE_FILE_INFORMATION info;
+  DWORD fake_inode;
+  int permission;
+
+  switch (GetFileType (fh) & ~FILE_TYPE_REMOTE)
+    {
+    case FILE_TYPE_DISK:
+      buf->st_mode = _S_IFREG;
+      if (!GetFileInformationByHandle (fh, &info))
+       {
+         errno = EACCES;
+         return -1;
+       }
+      break;
+    case FILE_TYPE_PIPE:
+      buf->st_mode = _S_IFIFO;
+      goto non_disk;
+    case FILE_TYPE_CHAR:
+    case FILE_TYPE_UNKNOWN:
+    default:
+      buf->st_mode = _S_IFCHR;
+    non_disk:
+      memset (&info, 0, sizeof (info));
+      info.dwFileAttributes = 0;
+      info.ftCreationTime = utc_base_ft;
+      info.ftLastAccessTime = utc_base_ft;
+      info.ftLastWriteTime = utc_base_ft;
+    }
+
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+      buf->st_mode = _S_IFDIR;
+
+  buf->st_nlink = info.nNumberOfLinks;
+  /* Might as well use file index to fake inode values, but this
+     is not guaranteed to be unique unless we keep a handle open
+     all the time (even then there are situations where it is
+     not unique).  Reputedly, there are at most 48 bits of info
+     (on NTFS, presumably less on FAT). */
+  fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh;
+
+  /* MSVC defines _ino_t to be short; other libc's might not.  */
+  if (sizeof (buf->st_ino) == 2)
+    buf->st_ino = fake_inode ^ (fake_inode >> 16);
+  else
+    buf->st_ino = fake_inode;
+
+  /* consider files to belong to current user */
+  buf->st_uid = 0;
+  buf->st_gid = 0;
+
+  buf->st_dev = info.dwVolumeSerialNumber;
+  buf->st_rdev = info.dwVolumeSerialNumber;
+
+  buf->st_size = info.nFileSizeLow;
+
+  /* Convert timestamps to Unix format. */
+  buf->st_mtime = convert_time (info.ftLastWriteTime);
+  buf->st_atime = convert_time (info.ftLastAccessTime);
+  if (buf->st_atime == 0) buf->st_atime = buf->st_mtime;
+  buf->st_ctime = convert_time (info.ftCreationTime);
+  if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime;
+
+  /* determine rwx permissions */
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+    permission = _S_IREAD;
+  else
+    permission = _S_IREAD | _S_IWRITE;
+  
+  if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+    permission |= _S_IEXEC;
   else
     {
+#if 0 /* no way of knowing the filename */
       char * p = strrchr (name, '.');
-      if (p != NULL
-         && (stricmp (p, ".exe") == 0 ||
-             stricmp (p, ".com") == 0 ||
-             stricmp (p, ".bat") == 0 ||
-             stricmp (p, ".cmd") == 0))
+      if (p != NULL &&
+         (stricmp (p, ".exe") == 0 ||
+          stricmp (p, ".com") == 0 ||
+          stricmp (p, ".bat") == 0 ||
+          stricmp (p, ".cmd") == 0))
        permission |= _S_IEXEC;
+#endif
     }
 
   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
@@ -1811,6 +2357,43 @@ stat (const char * path, struct stat * buf)
   return 0;
 }
 
+int
+utime (const char *name, struct utimbuf *times)
+{
+  struct utimbuf deftime;
+  HANDLE fh;
+  FILETIME mtime;
+  FILETIME atime;
+
+  if (times == NULL)
+    {
+      deftime.modtime = deftime.actime = time (NULL);
+      times = &deftime;
+    }
+
+  /* Need write access to set times.  */
+  fh = CreateFile (name, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                  0, OPEN_EXISTING, 0, NULL);
+  if (fh)
+    {
+      convert_from_time_t (times->actime, &atime);
+      convert_from_time_t (times->modtime, &mtime);
+      if (!SetFileTime (fh, NULL, &atime, &mtime))
+       {
+         CloseHandle (fh);
+         errno = EACCES;
+         return -1;
+       }
+      CloseHandle (fh);
+    }
+  else
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  return 0;
+}
+
 #ifdef HAVE_SOCKETS
 
 /* Wrappers for  winsock functions to map between our file descriptors
@@ -1841,7 +2424,18 @@ unsigned long (PASCAL *pfn_inet_addr) (const char * cp);
 int (PASCAL *pfn_gethostname) (char * name, int namelen);
 struct hostent * (PASCAL *pfn_gethostbyname) (const char * name);
 struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto);
-  
+int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen);
+int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname,
+                             const char * optval, int optlen);
+int (PASCAL *pfn_listen) (SOCKET s, int backlog);
+int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name,
+                              int * namelen);
+SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen);
+int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags,
+                      struct sockaddr * from, int * fromlen);
+int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags,
+                         const struct sockaddr * to, int tolen);
+
 /* SetHandleInformation is only needed to make sockets non-inheritable. */
 BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags);
 #ifndef HANDLE_FLAG_INHERIT
@@ -1910,8 +2504,14 @@ init_winsock (int load_now)
       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 */
@@ -1977,6 +2577,99 @@ static void check_errno ()
     pfn_WSASetLastError (0);
 }
 
+/* Extend strerror to handle the winsock-specific error codes.  */
+struct {
+  int errnum;
+  char * msg;
+} _wsa_errlist[] = {
+  WSAEINTR                , "Interrupted function call",
+  WSAEBADF                , "Bad file descriptor",
+  WSAEACCES               , "Permission denied",
+  WSAEFAULT               , "Bad address",
+  WSAEINVAL               , "Invalid argument",
+  WSAEMFILE               , "Too many open files",
+                       
+  WSAEWOULDBLOCK          , "Resource temporarily unavailable",
+  WSAEINPROGRESS          , "Operation now in progress",
+  WSAEALREADY             , "Operation already in progress",
+  WSAENOTSOCK             , "Socket operation on non-socket",
+  WSAEDESTADDRREQ         , "Destination address required",
+  WSAEMSGSIZE             , "Message too long",
+  WSAEPROTOTYPE           , "Protocol wrong type for socket",
+  WSAENOPROTOOPT          , "Bad protocol option",
+  WSAEPROTONOSUPPORT      , "Protocol not supported",
+  WSAESOCKTNOSUPPORT      , "Socket type not supported",
+  WSAEOPNOTSUPP           , "Operation not supported",
+  WSAEPFNOSUPPORT         , "Protocol family not supported",
+  WSAEAFNOSUPPORT         , "Address family not supported by protocol family",
+  WSAEADDRINUSE           , "Address already in use",
+  WSAEADDRNOTAVAIL        , "Cannot assign requested address",
+  WSAENETDOWN             , "Network is down",
+  WSAENETUNREACH          , "Network is unreachable",
+  WSAENETRESET            , "Network dropped connection on reset",
+  WSAECONNABORTED         , "Software caused connection abort",
+  WSAECONNRESET           , "Connection reset by peer",
+  WSAENOBUFS              , "No buffer space available",
+  WSAEISCONN              , "Socket is already connected",
+  WSAENOTCONN             , "Socket is not connected",
+  WSAESHUTDOWN            , "Cannot send after socket shutdown",
+  WSAETOOMANYREFS         , "Too many references",         /* not sure */
+  WSAETIMEDOUT            , "Connection timed out",
+  WSAECONNREFUSED         , "Connection refused",
+  WSAELOOP                , "Network loop",                /* not sure */
+  WSAENAMETOOLONG         , "Name is too long",
+  WSAEHOSTDOWN            , "Host is down",
+  WSAEHOSTUNREACH         , "No route to host",
+  WSAENOTEMPTY            , "Buffer not empty",                    /* not sure */
+  WSAEPROCLIM             , "Too many processes",
+  WSAEUSERS               , "Too many users",              /* not sure */
+  WSAEDQUOT               , "Double quote in host name",    /* really not sure */
+  WSAESTALE               , "Data is stale",               /* not sure */
+  WSAEREMOTE              , "Remote error",                /* not sure */
+                       
+  WSASYSNOTREADY          , "Network subsystem is unavailable",
+  WSAVERNOTSUPPORTED      , "WINSOCK.DLL version out of range",
+  WSANOTINITIALISED       , "Winsock not initialized successfully",
+  WSAEDISCON              , "Graceful shutdown in progress",
+#ifdef WSAENOMORE
+  WSAENOMORE              , "No more operations allowed",   /* not sure */
+  WSAECANCELLED           , "Operation cancelled",         /* not sure */
+  WSAEINVALIDPROCTABLE    , "Invalid procedure table from service provider",
+  WSAEINVALIDPROVIDER     , "Invalid service provider version number",
+  WSAEPROVIDERFAILEDINIT  , "Unable to initialize a service provider",
+  WSASYSCALLFAILURE       , "System call failured",
+  WSASERVICE_NOT_FOUND    , "Service not found",           /* not sure */
+  WSATYPE_NOT_FOUND       , "Class type not found",
+  WSA_E_NO_MORE           , "No more resources available",  /* really not sure */
+  WSA_E_CANCELLED         , "Operation already cancelled",  /* really not sure */
+  WSAEREFUSED             , "Operation refused",           /* not sure */
+#endif
+                       
+  WSAHOST_NOT_FOUND       , "Host not found",
+  WSATRY_AGAIN            , "Authoritative host not found during name lookup",
+  WSANO_RECOVERY          , "Non-recoverable error during name lookup",
+  WSANO_DATA              , "Valid name, no data record of requested type",
+
+  -1, NULL
+};
+
+char *
+sys_strerror(int error_no)
+{
+  int i;
+  static char unknown_msg[40];
+
+  if (error_no >= 0 && error_no < sys_nerr)
+    return sys_errlist[error_no];
+
+  for (i = 0; _wsa_errlist[i].errnum >= 0; i++)
+    if (_wsa_errlist[i].errnum == error_no)
+      return _wsa_errlist[i].msg;
+
+  sprintf(unknown_msg, "Unidentified error: %d", error_no);
+  return unknown_msg;
+}
+
 /* [andrewi 3-May-96] I've had conflicting results using both methods,
    but I believe the method of keeping the socket handle separate (and
    insuring it is not inheritable) is the correct one. */
@@ -1989,12 +2682,12 @@ static void check_errno ()
 #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)
 {
-  int fd;
-  long s;
-  child_process * cp;
+  SOCKET s;
 
   if (winsock_lib == NULL)
     {
@@ -2005,89 +2698,114 @@ sys_socket(int af, int type, int protocol)
   check_errno ();
 
   /* call the real socket function */
-  s = (long) pfn_socket (af, type, protocol);
+  s = pfn_socket (af, type, protocol);
   
   if (s != INVALID_SOCKET)
-    {
-      /* Although under NT 3.5 _open_osfhandle will accept a socket
-        handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
-        that does not work under NT 3.1.  However, we can get the same
-        effect by using a backdoor function to replace an existing
-        descriptor handle with the one we want. */
+    return socket_to_fd (s);
 
-      /* allocate a file descriptor (with appropriate flags) */
-      fd = _open ("NUL:", _O_RDWR);
-      if (fd >= 0)
-        {
+  set_errno ();
+  return -1;
+}
+
+/* Convert a SOCKET to a file descriptor.  */
+int
+socket_to_fd (SOCKET s)
+{
+  int fd;
+  child_process * cp;
+
+  /* Although under NT 3.5 _open_osfhandle will accept a socket
+     handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT,
+     that does not work under NT 3.1.  However, we can get the same
+     effect by using a backdoor function to replace an existing
+     descriptor handle with the one we want. */
+
+  /* allocate a file descriptor (with appropriate flags) */
+  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); */
+      /* 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. */
+      /* 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
+        the MS Proxy winsock client is installed.  */
+      {
+       /* Apparently there is a bug in NT 3.51 with some service
+          packs, which prevents using DuplicateHandle to make a
+          socket handle non-inheritable (causes WSACleanup to
+          hang).  The work-around is to use SetHandleInformation
+          instead if it is available and implemented. */
+       if (pfn_SetHandleInformation)
+         {
+           pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
+         }
+       else
          {
-           HANDLE parent;
+           HANDLE parent = GetCurrentProcess ();
            HANDLE new_s = INVALID_HANDLE_VALUE;
 
-           parent = GetCurrentProcess ();
-
-           /* Apparently there is a bug in NT 3.51 with some service
-              packs, which prevents using DuplicateHandle to make a
-              socket handle non-inheritable (causes WSACleanup to
-              hang).  The work-around is to use SetHandleInformation
-              instead if it is available and implemented. */
-           if (!pfn_SetHandleInformation
-               || !pfn_SetHandleInformation ((HANDLE) s,
-                                             HANDLE_FLAG_INHERIT,
-                                             HANDLE_FLAG_INHERIT))
-             {
-               DuplicateHandle (parent,
+           if (DuplicateHandle (parent,
                                 (HANDLE) s,
                                 parent,
                                 &new_s,
                                 0,
                                 FALSE,
-                                DUPLICATE_SAME_ACCESS);
-               pfn_closesocket (s);
-               s = (SOCKET) new_s;
-             }
-           fd_info[fd].hnd = (HANDLE) s;
+                                DUPLICATE_SAME_ACCESS))
+             {
+               /* It is possible that DuplicateHandle succeeds even
+                  though the socket wasn't really a kernel handle,
+                  because a real handle has the same value.  So
+                  test whether the new handle really is a socket.  */
+               long nonblocking = 0;
+               if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0)
+                 {
+                   pfn_closesocket (s);
+                   s = (SOCKET) new_s;
+                 }
+               else
+                 {
+                   CloseHandle (new_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;
-
-         cp = new_child ();
-         if (cp)
-           {
-             cp->fd = fd;
-             cp->status = STATUS_READ_ACKNOWLEDGED;
-
-             /* attach child_process to fd_info */
-             if (fd_info[ fd ].cp != NULL)
-               {
-                 DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
-                 abort ();
-               }
+      /* set our own internal flags */
+      fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE;
 
-             fd_info[ fd ].cp = cp;
+      cp = new_child ();
+      if (cp)
+       {
+         cp->fd = fd;
+         cp->status = STATUS_READ_ACKNOWLEDGED;
 
-             /* success! */
-             winsock_inuse++;  /* count open sockets */
-             return fd;
+         /* attach child_process to fd_info */
+         if (fd_info[ fd ].cp != NULL)
+           {
+             DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd));
+             abort ();
            }
 
-         /* clean up */
-         _close (fd);
+         fd_info[ fd ].cp = cp;
+
+         /* success! */
+         winsock_inuse++;      /* count open sockets */
+         return fd;
        }
-      pfn_closesocket (s);
-      h_errno = EMFILE;
-    }
-  set_errno ();
 
+      /* clean up */
+      _close (fd);
+    }
+  pfn_closesocket (s);
+  h_errno = EMFILE;
   return -1;
 }
 
@@ -2163,7 +2881,7 @@ sys_gethostname (char * name, int namelen)
     return pfn_gethostname (name, namelen);
 
   if (namelen > MAX_COMPUTERNAME_LENGTH)
-    return !GetComputerName (name, &namelen);
+    return !GetComputerName (name, (DWORD *)&namelen);
 
   h_errno = EFAULT;
   return SOCKET_ERROR;
@@ -2206,10 +2924,30 @@ sys_getservbyname(const char * name, const char * proto)
 }
 
 int
-sys_shutdown (int s, int how)
+sys_getpeername (int s, struct sockaddr *addr, int * namelen)
 {
-  int rc;
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;
+}
+
 
+int
+sys_shutdown (int s, int how)
+{
   if (winsock_lib == NULL)
     {
       h_errno = ENETDOWN;
@@ -2228,6 +2966,171 @@ sys_shutdown (int s, int how)
   return SOCKET_ERROR;
 }
 
+int
+sys_setsockopt (int s, int level, int optname, const char * optval, int optlen)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname,
+                              optval, optlen);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;      
+}
+
+int
+sys_listen (int s, int backlog)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_listen (SOCK_HANDLE (s), backlog);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;      
+}
+
+int
+sys_getsockname (int s, struct sockaddr * name, int * namelen)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;      
+}
+
+int
+sys_accept (int s, struct sockaddr * addr, int * addrlen)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return -1;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
+      if (t != INVALID_SOCKET)
+       return socket_to_fd (t);
+
+      set_errno ();
+      return -1;
+    }
+  h_errno = ENOTSOCK;
+  return -1;
+}
+
+int
+sys_recvfrom (int s, char * buf, int len, int flags,
+         struct sockaddr * from, int * fromlen)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;
+}
+
+int
+sys_sendto (int s, const char * buf, int len, int flags,
+           const struct sockaddr * to, int tolen)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return SOCKET_ERROR;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen);
+      if (rc == SOCKET_ERROR)
+       set_errno ();
+      return rc;
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;
+}
+
+/* Windows does not have an fcntl function.  Provide an implementation
+   solely for making sockets non-blocking.  */
+int
+fcntl (int s, int cmd, int options)
+{
+  if (winsock_lib == NULL)
+    {
+      h_errno = ENETDOWN;
+      return -1;
+    }
+
+  check_errno ();
+  if (fd_info[s].flags & FILE_SOCKET)
+    {
+      if (cmd == F_SETFL && options == O_NDELAY)
+       {
+         unsigned long nblock = 1;
+         int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock);
+         if (rc == SOCKET_ERROR)
+           set_errno();
+         /* Keep track of the fact that we set this to non-blocking.  */
+         fd_info[s].flags |= FILE_NDELAY;
+         return rc;
+       }
+      else
+       {
+         h_errno = EINVAL;
+         return SOCKET_ERROR;
+       }
+    }
+  h_errno = ENOTSOCK;
+  return SOCKET_ERROR;
+}
+
 #endif /* HAVE_SOCKETS */
 
 
@@ -2338,7 +3241,6 @@ sys_pipe (int * phandles)
 {
   int rc;
   unsigned flags;
-  child_process * cp;
 
   /* make pipe handles non-inheritable; when we spawn a child, we
      replace the relevant handle with an inheritable one.  Also put
@@ -2412,7 +3314,20 @@ _sys_read_ahead (int fd)
     }
 #ifdef HAVE_SOCKETS
   else if (fd_info[fd].flags & FILE_SOCKET)
-    rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
+    {
+      unsigned long nblock = 0;
+      /* We always want this to block, so temporarily disable NDELAY.  */
+      if (fd_info[fd].flags & FILE_NDELAY)
+       pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+
+      rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0);
+
+      if (fd_info[fd].flags & FILE_NDELAY)
+       {
+         nblock = 1;
+         pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+       }
+    }
 #endif
   
   if (rc == sizeof (char))
@@ -2650,40 +3565,49 @@ check_windows_init_file ()
      it cannot find the Windows installation file.  If this file does
      not exist in the expected place, tell the user.  */
 
-  if (!noninteractive && !inhibit_window_system) {
-    extern Lisp_Object Vwindow_system, Vload_path;
-    Lisp_Object init_file;
-    int fd;
-
-    init_file = build_string ("term/w32-win");
-    fd = openp (Vload_path, init_file, ".el:.elc", NULL, 0);
-    if (fd < 0) {
-      Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
-      char *init_file_name = XSTRING (init_file)->data;
-      char *load_path = XSTRING (load_path_print)->data;
-      char *buffer = alloca (1024);
-
-      sprintf (buffer, 
-              "The Emacs Windows initialization file \"%s.el\" "
-              "could not be found in your Emacs installation.  "
-              "Emacs checked the following directories for this file:\n"
-              "\n%s\n\n"
-              "When Emacs cannot find this file, it usually means that it "
-              "was not installed properly, or its distribution file was "
-              "not unpacked properly.\nSee the README.W32 file in the "
-              "top-level Emacs directory for more information.",
-              init_file_name, load_path);
-      MessageBox (NULL,
-                 buffer,
-                 "Emacs Abort Dialog",
-                 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
-      close (fd);
+  if (!noninteractive && !inhibit_window_system) 
+    {
+      extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p;
+      Lisp_Object objs[2];
+      Lisp_Object full_load_path;
+      Lisp_Object init_file;
+      int fd;
 
+      objs[0] = Vload_path;
+      objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH")));
+      full_load_path = Fappend (2, objs);
+      init_file = build_string ("term/w32-win");
+      fd = openp (full_load_path, init_file, Vload_suffixes, NULL, Qnil);
+      if (fd < 0) 
+       {
+         Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil);
+         char *init_file_name = SDATA (init_file);
+         char *load_path = SDATA (load_path_print);
+         char *buffer = alloca (1024);
+
+         sprintf (buffer, 
+                  "The Emacs Windows initialization file \"%s.el\" "
+                  "could not be found in your Emacs installation.  "
+                  "Emacs checked the following directories for this file:\n"
+                  "\n%s\n\n"
+                  "When Emacs cannot find this file, it usually means that it "
+                  "was not installed properly, or its distribution file was "
+                  "not unpacked properly.\nSee the README.W32 file in the "
+                  "top-level Emacs directory for more information.",
+                  init_file_name, load_path);
+         MessageBox (NULL,
+                     buffer,
+                     "Emacs Abort Dialog",
+                     MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
       /* Use the low-level Emacs abort. */
 #undef abort
-      abort ();
+         abort ();
+       }
+      else
+       {
+         _close (fd);
+       }
     }
-  }
 }
 
 void
@@ -2693,12 +3617,6 @@ term_ntproc ()
   /* shutdown the socket interface if necessary */
   term_winsock ();
 #endif
-
-  /* Check whether we are shutting down because we cannot find the
-     Windows initialization file.  Do this during shutdown so that
-     Emacs is initialized as possible, and so that it is out of the 
-     critical startup path.  */
-  check_windows_init_file ();
 }
 
 void
@@ -2797,7 +3715,13 @@ init_ntproc ()
 
       (*drive)++;
     }
+
+    /* Reset the volume info cache.  */
+    volume_cache = NULL;
   }
+  
+  /* Check to see if Emacs has been installed correctly.  */
+  check_windows_init_file ();
 }
 
 /* end of nt.c */