(sys_write): Avoid non-blocking mode, which is not fully
[bpt/emacs.git] / src / w32.c
index d4bfca2..c7537ec 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.
 
@@ -35,7 +35,11 @@ Boston, MA 02111-1307, USA.
 #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
@@ -80,7 +84,14 @@ 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"
@@ -88,17 +99,177 @@ Boston, MA 02111-1307, USA.
 #include "w32heap.h"
 #include "systime.h"
 
-#undef min
-#undef max
-#define min(x, y) (((x) < (y)) ? (x) : (y))
-#define max(x, y) (((x) > (y)) ? (x) : (y))
-
 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
+/*
+  BEGIN: Wrapper functions around OpenProcessToken
+  and other functions in advapi32.dll that are only
+  supported in Windows NT / 2k / XP
+*/
+  /* ** Function pointer typedefs ** */
+typedef BOOL (WINAPI * OpenProcessToken_Proc) (
+    HANDLE ProcessHandle,
+    DWORD DesiredAccess,
+    PHANDLE TokenHandle);
+typedef BOOL (WINAPI * GetTokenInformation_Proc) (
+    HANDLE TokenHandle,
+    TOKEN_INFORMATION_CLASS TokenInformationClass,
+    LPVOID TokenInformation,
+    DWORD TokenInformationLength,
+    PDWORD ReturnLength);
+#ifdef _UNICODE
+const char * const LookupAccountSid_Name = "LookupAccountSidW";
+#else
+const char * const LookupAccountSid_Name = "LookupAccountSidA";
+#endif
+typedef BOOL (WINAPI * LookupAccountSid_Proc) (
+    LPCTSTR lpSystemName,
+    PSID Sid,
+    LPTSTR Name,
+    LPDWORD cbName,
+    LPTSTR DomainName,
+    LPDWORD cbDomainName,
+    PSID_NAME_USE peUse);
+typedef PSID_IDENTIFIER_AUTHORITY (WINAPI * GetSidIdentifierAuthority_Proc) (
+    PSID pSid);
+
+  /* ** A utility function ** */
+static BOOL is_windows_9x ()
+{
+  BOOL b_ret=0;
+  OSVERSIONINFO os_ver;
+  ZeroMemory(&os_ver, sizeof(OSVERSIONINFO));
+  os_ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+  if (GetVersionEx (&os_ver))
+    {
+      b_ret = (os_ver.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
+    }
+  return b_ret;
+}
+
+  /* ** The wrapper functions ** */
+
+BOOL WINAPI open_process_token (
+    HANDLE ProcessHandle,
+    DWORD DesiredAccess,
+    PHANDLE TokenHandle)
+{
+  OpenProcessToken_Proc pfn_Open_Process_Token = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  hm_advapi32 = LoadLibrary ("Advapi32.dll");
+  pfn_Open_Process_Token =
+    (OpenProcessToken_Proc) GetProcAddress (hm_advapi32, "OpenProcessToken");
+  if (pfn_Open_Process_Token == NULL)
+    {
+      return FALSE;
+    }
+  return (
+      pfn_Open_Process_Token (
+          ProcessHandle,
+          DesiredAccess,
+          TokenHandle)
+      );
+}
+
+BOOL WINAPI get_token_information (
+    HANDLE TokenHandle,
+    TOKEN_INFORMATION_CLASS TokenInformationClass,
+    LPVOID TokenInformation,
+    DWORD TokenInformationLength,
+    PDWORD ReturnLength)
+{
+  GetTokenInformation_Proc pfn_Get_Token_Information = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  hm_advapi32 = LoadLibrary ("Advapi32.dll");
+  pfn_Get_Token_Information =
+    (GetTokenInformation_Proc) GetProcAddress (hm_advapi32, "GetTokenInformation");
+  if (pfn_Get_Token_Information == NULL)
+    {
+      return FALSE;
+    }
+  return (
+      pfn_Get_Token_Information (
+          TokenHandle,
+          TokenInformationClass,
+          TokenInformation,
+          TokenInformationLength,
+          ReturnLength)
+      );
+}
+
+BOOL WINAPI lookup_account_sid (
+    LPCTSTR lpSystemName,
+    PSID Sid,
+    LPTSTR Name,
+    LPDWORD cbName,
+    LPTSTR DomainName,
+    LPDWORD cbDomainName,
+    PSID_NAME_USE peUse)
+{
+  LookupAccountSid_Proc pfn_Lookup_Account_Sid = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  hm_advapi32 = LoadLibrary ("Advapi32.dll");
+  pfn_Lookup_Account_Sid =
+    (LookupAccountSid_Proc) GetProcAddress (hm_advapi32, LookupAccountSid_Name);
+  if (pfn_Lookup_Account_Sid == NULL)
+    {
+      return FALSE;
+    }
+  return (
+      pfn_Lookup_Account_Sid (
+          lpSystemName,
+          Sid,
+          Name,
+          cbName,
+          DomainName,
+          cbDomainName,
+          peUse)
+      );
+}
+
+PSID_IDENTIFIER_AUTHORITY WINAPI get_sid_identifier_authority (
+    PSID pSid)
+{
+  GetSidIdentifierAuthority_Proc pfn_Get_Sid_Identifier_Authority = NULL;
+  HMODULE hm_advapi32 = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return NULL;
+    }
+  hm_advapi32 = LoadLibrary ("Advapi32.dll");
+  pfn_Get_Sid_Identifier_Authority =
+    (GetSidIdentifierAuthority_Proc) GetProcAddress (
+        hm_advapi32, "GetSidIdentifierAuthority");
+  if (pfn_Get_Sid_Identifier_Authority == NULL)
+    {
+      return NULL;
+    }
+  return (pfn_Get_Sid_Identifier_Authority (pSid));
+}
+
+/*
+  END: Wrapper functions around OpenProcessToken
+  and other functions in advapi32.dll that are only
+  supported in Windows NT / 2k / XP
+*/
+
+\f
 /* Equivalent of strerror for W32 error codes.  */
 char *
 w32_strerror (int error_no)
@@ -248,11 +419,15 @@ init_user_info ()
   HANDLE          token = NULL;
   SID_NAME_USE    user_type;
 
-  if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token)
-      && GetTokenInformation (token, TokenUser,
+  if (
+                       open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
+      && get_token_information (
+                                       token, TokenUser,
                              (PVOID) user_sid, sizeof (user_sid), &trash)
-      && LookupAccountSid (NULL, *((PSID *) user_sid), name, &length,
-                          domain, &dlength, &user_type))
+      && lookup_account_sid (
+                                       NULL, *((PSID *) user_sid), name, &length,
+                          domain, &dlength, &user_type)
+                       )
     {
       strcpy (the_passwd.pw_name, name);
       /* Determine a reasonable uid value. */
@@ -265,7 +440,7 @@ init_user_info ()
        {
          SID_IDENTIFIER_AUTHORITY * pSIA;
 
-         pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
+         pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
          /* I believe the relative portion is the last 4 bytes (of 6)
             with msb first. */
          the_passwd.pw_uid = ((pSIA->Value[2] << 24) +
@@ -276,12 +451,12 @@ init_user_info ()
          the_passwd.pw_uid = the_passwd.pw_uid % 60001;
 
          /* Get group id */
-         if (GetTokenInformation (token, TokenPrimaryGroup,
+         if (get_token_information (token, TokenPrimaryGroup,
                                   (PVOID) user_sid, sizeof (user_sid), &trash))
            {
              SID_IDENTIFIER_AUTHORITY * pSIA;
 
-             pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid));
+             pSIA = get_sid_identifier_authority (*((PSID *) user_sid));
              the_passwd.pw_gid = ((pSIA->Value[2] << 24) +
                                   (pSIA->Value[3] << 16) +
                                   (pSIA->Value[4] << 8)  +
@@ -704,7 +879,7 @@ init_environment (char ** argv)
        {
          char * var = alloca (strlen (tmp) + 8);
          sprintf (var, "TMPDIR=%s", tmp);
-         _putenv (var);
+         _putenv (strdup (var));
          break;
        }
     }
@@ -736,7 +911,6 @@ init_environment (char ** argv)
       {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"},
       {"EMACSDATA", "%emacs_dir%/etc"},
       {"EMACSPATH", "%emacs_dir%/bin"},
-      {"EMACSLOCKDIR", "%emacs_dir%/lock"},
       /* We no longer set INFOPATH because Info-default-directory-list
         is then ignored.  */
       /*  {"INFOPATH", "%emacs_dir%/info"},  */
@@ -880,6 +1054,21 @@ init_environment (char ** argv)
   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
@@ -1819,8 +2008,14 @@ 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
@@ -2127,16 +2322,11 @@ 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)
-          /* No access rights required to get info.  */
-          && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, NULL))
-             != INVALID_HANDLE_VALUE)
+  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
@@ -2159,25 +2349,33 @@ stat (const char * path, struct stat * buf)
          fake_inode = 0;
        }
 
-      switch (GetFileType (fh))
+      if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
        {
-       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_mode = _S_IFDIR;
+       }
+      else
+       {
+         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
     {
       /* 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;
     }
@@ -2270,21 +2468,15 @@ fstat (int desc, struct stat * buf)
     }
 
   if (info.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
-    {
-      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;
-    }
+
+  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)
@@ -2401,7 +2593,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
@@ -2470,8 +2673,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 */
@@ -2642,12 +2851,12 @@ sys_strerror(int error_no)
 #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)
     {
@@ -2658,105 +2867,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.  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.  */
+      /* 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)
          {
-           /* 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
+           pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
+         }
+       else
+         {
+           HANDLE parent = GetCurrentProcess ();
+           HANDLE new_s = INVALID_HANDLE_VALUE;
+
+           if (DuplicateHandle (parent,
+                                (HANDLE) s,
+                                parent,
+                                &new_s,
+                                0,
+                                FALSE,
+                                DUPLICATE_SAME_ACCESS))
              {
-               HANDLE parent = GetCurrentProcess ();
-               HANDLE new_s = INVALID_HANDLE_VALUE;
-
-               if (DuplicateHandle (parent,
-                                    (HANDLE) s,
-                                    parent,
-                                    &new_s,
-                                    0,
-                                    FALSE,
-                                    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)
                  {
-                   /* 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);
-                     }
-                 } 
-             }
+                   pfn_closesocket (s);
+                   s = (SOCKET) new_s;
+                 }
+               else
+                 {
+                   CloseHandle (new_s);
+                 }
+             } 
          }
-         fd_info[fd].hnd = (HANDLE) 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;
 }
 
@@ -2874,6 +3092,28 @@ sys_getservbyname(const char * name, const char * proto)
   return serv;
 }
 
+int
+sys_getpeername (int s, struct sockaddr *addr, int * namelen)
+{
+  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)
 {
@@ -2895,6 +3135,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 */
 
 
@@ -3078,7 +3483,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))
@@ -3291,8 +3709,24 @@ sys_write (int fd, const void * buffer, unsigned int count)
 #ifdef HAVE_SOCKETS
   if (fd_info[fd].flags & FILE_SOCKET)
     {
+      unsigned long nblock = 0;
       if (winsock_lib == NULL) abort ();
+
+      /* TODO: implement select() properly so non-blocking I/O works. */
+      /* For now, make sure the write blocks.  */
+      if (fd_info[fd].flags & FILE_NDELAY)
+       pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+
       nchars =  pfn_send (SOCK_HANDLE (fd), buffer, count, 0);
+
+      /* Set the socket back to non-blocking if it was before,
+        for other operations that support it.  */
+      if (fd_info[fd].flags & FILE_NDELAY)
+       {
+         nblock = 1;
+         pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock);
+       }
+
       if (nchars == SOCKET_ERROR)
         {
          DebPrint(("sys_read.send failed with error %d on socket %ld\n",
@@ -3328,12 +3762,12 @@ check_windows_init_file ()
       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);
+      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 = XSTRING (init_file)->data;
-         char *load_path = XSTRING (load_path_print)->data;
+         char *init_file_name = SDATA (init_file);
+         char *load_path = SDATA (load_path_print);
          char *buffer = alloca (1024);
 
          sprintf (buffer,