(Fexpand_abbrev): Remove unused variables.
[bpt/emacs.git] / src / w32.c
index 42027ae..7a9f866 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -30,6 +30,7 @@ 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>
 
@@ -56,6 +57,8 @@ Boston, MA 02111-1307, USA.
 #undef read
 #undef write
 
+#undef strerror
+
 #include "lisp.h"
 
 #include <pwd.h>
@@ -464,6 +467,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 illegal characters */
+  if (strpbrk (name, "*?|<>\""))
+    return 0;
+
   dir_handle = FindFirstFile (name, &find_data);
   if (dir_handle != INVALID_HANDLE_VALUE)
     {
@@ -498,9 +505,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, '\\');
@@ -523,11 +531,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.  */
 
@@ -621,8 +641,44 @@ char *get_emacs_configuration (void);
 extern Lisp_Object Vsystem_configuration;
 
 void
-init_environment ()
+init_environment (char ** argv)
 {
+  int len;
+  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 (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 if they don't exist */
   {
     int i;
@@ -704,7 +760,7 @@ init_environment ()
 
   {
     char *p;
-    char modname[MAX_PATH];
+    static char modname[MAX_PATH];
 
     if (!GetModuleFileName (NULL, modname, MAX_PATH))
       abort ();
@@ -713,6 +769,10 @@ init_environment ()
     *p = 0;
 
     SetCurrentDirectory (modname);
+
+    /* Ensure argv[0] has the full path to Emacs.  */
+    *p = '\\';
+    argv[0] = modname;
   }
 
   init_user_info ();
@@ -729,6 +789,7 @@ char *
 get_emacs_configuration (void)
 {
   char *arch, *oem, *os;
+  int build_num;
 
   /* Determine the processor type.  */
   switch (get_processor_type ()) 
@@ -770,10 +831,37 @@ get_emacs_configuration (void)
   /* Let oem be "*" until we figure out how to decode the OEM field.  */
   oem = "*";
 
-  os = (GetVersion () & OS_WIN95) ? "windows95" : "nt";
+  switch (osinfo_cache.dwPlatformId) {
+  case VER_PLATFORM_WIN32_NT:
+    os = "nt";
+    build_num = osinfo_cache.dwBuildNumber;
+    break;
+  case VER_PLATFORM_WIN32_WINDOWS:
+    if (osinfo_cache.dwMinorVersion == 0) {
+      os = "windows95";
+    } else {
+      os = "windows98";
+    }
+    build_num = LOWORD (osinfo_cache.dwBuildNumber);
+    break;
+  case VER_PLATFORM_WIN32s:
+    /* Not supported, should not happen. */
+    os = "windows32s";
+    build_num = LOWORD (osinfo_cache.dwBuildNumber);
+    break;
+  default:
+    os = "unknown";
+    build_num = 0;
+    break;
+  }
+
+  if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) {
+    sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os,
+            get_w32_major_version (), get_w32_minor_version (), build_num);
+  } else {
+    sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num);
+  }
 
-  sprintf (configuration_buffer, "%s-%s-%s%d.%d", arch, oem, os,
-          get_w32_major_version (), get_w32_minor_version ());
   return configuration_buffer;
 }
 
@@ -1131,6 +1219,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.  */
@@ -1141,6 +1241,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)
 {
@@ -1149,10 +1256,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;
@@ -1174,14 +1291,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;
@@ -1227,6 +1356,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)
+{
+  int count;
+  int result;
+  int 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
@@ -1236,7 +1439,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
@@ -1440,8 +1677,8 @@ sys_open (const char * path, int oflag, int mode)
 int
 sys_rename (const char * oldname, const char * newname)
 {
+  int 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
@@ -1460,37 +1697,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 || errno == EACCES));
+      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 || errno == EACCES)
+      && _chmod (newname, 0666) == 0
+      && _unlink (newname) == 0)
+    result = rename (temp, newname);
 
-  return rename (temp, newname);
+  return result;
 }
 
 int
@@ -1624,7 +1880,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;
@@ -1639,13 +1895,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 illegal 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. */
@@ -1654,7 +1917,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, "\\");
@@ -1678,9 +1955,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)
        {
@@ -1705,31 +1984,18 @@ stat (const char * path, struct stat * buf)
       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))
+  else if (!NILP (Vw32_get_true_file_attributes)
+          /* No access rights required to get info.  */
+          && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, 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))
        {
-         switch (GetFileType (fh))
-           {
-           case FILE_TYPE_DISK:
-             buf->st_mode = _S_IFREG;
-             break;
-           case FILE_TYPE_PIPE:
-             buf->st_mode = _S_IFIFO;
-             break;
-           case FILE_TYPE_CHAR:
-           case FILE_TYPE_UNKNOWN:
-           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
@@ -1737,13 +2003,27 @@ stat (const char * path, struct stat * buf)
             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;
+         buf->st_nlink = 1;
+         fake_inode = 0;
+       }
+
+      switch (GetFileType (fh))
+       {
+       case FILE_TYPE_DISK:
+         buf->st_mode = _S_IFREG;
+         break;
+       case FILE_TYPE_PIPE:
+         buf->st_mode = _S_IFIFO;
+         break;
+       case FILE_TYPE_CHAR:
+       case FILE_TYPE_UNKNOWN:
+       default:
+         buf->st_mode = _S_IFCHR;
        }
+      CloseHandle (fh);
     }
   else
     {
@@ -1797,16 +2077,8 @@ stat (const char * path, struct stat * buf)
   
   if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
     permission |= _S_IEXEC;
-  else
-    {
-      char * p = strrchr (name, '.');
-      if (p != NULL
-         && (stricmp (p, ".exe") == 0 ||
-             stricmp (p, ".com") == 0 ||
-             stricmp (p, ".bat") == 0 ||
-             stricmp (p, ".cmd") == 0))
-       permission |= _S_IEXEC;
-    }
+  else if (is_exec (name))
+    permission |= _S_IEXEC;
 
   buf->st_mode |= permission | (permission >> 3) | (permission >> 6);
 
@@ -2116,6 +2388,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. */
@@ -2789,40 +3154,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, ".el:.elc", NULL, 0);
+      if (fd < 0) 
+       {
+         Lisp_Object load_path_print = Fprin1_to_string (full_load_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);
       /* Use the low-level Emacs abort. */
 #undef abort
-      abort ();
+         abort ();
+       }
+      else
+       {
+         close (fd);
+       }
     }
-  }
 }
 
 void
@@ -2832,12 +3206,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
@@ -2937,6 +3305,9 @@ init_ntproc ()
       (*drive)++;
     }
   }
+  
+  /* Check to see if Emacs has been installed correctly.  */
+  check_windows_init_file ();
 }
 
 /* end of nt.c */