(init_environment): Install code from 20.7 for providing
[bpt/emacs.git] / src / w32.c
index 8bf8f04..243948c 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -57,6 +57,8 @@ Boston, MA 02111-1307, USA.
 #undef read
 #undef write
 
+#undef strerror
+
 #include "lisp.h"
 
 #include <pwd.h>
@@ -89,6 +91,7 @@ Boston, MA 02111-1307, USA.
 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;
 
 static char startup_dir[MAXPATHLEN];
 
@@ -286,9 +289,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"));
@@ -465,7 +468,7 @@ 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 */
+  /* must be valid filename, no wild cards or other invalid characters */
   if (strpbrk (name, "*?|<>\""))
     return 0;
 
@@ -503,9 +506,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, '\\');
@@ -528,7 +532,6 @@ w32_get_long_filename (char * name, char * buf, int size)
       else
        return FALSE;
     }
-  while (p != NULL && *p);
 
   return TRUE;
 }
@@ -555,12 +558,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) 
 { 
@@ -639,7 +654,7 @@ char *get_emacs_configuration (void);
 extern Lisp_Object Vsystem_configuration;
 
 void
-init_environment ()
+init_environment (char ** argv)
 {
   int len;
   static const char * const tempdirs[] = {
@@ -677,52 +692,96 @@ init_environment ()
                     Qnil)),
        "While setting TMPDIR: ");
 
-  /* Check for environment variables and use registry if they don't exist */
+  /* 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;
 
-    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%/lisp;%emacs_dir%/leim"},
+      {"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.  We use a hook in winnt.el instead.  */
-      /*      "INFOPATH", */
-      "EMACSDOC",
-      "TERM",
+        is then ignored.  */
+      /*  {"INFOPATH", "%emacs_dir%/info"},  */
+      {"EMACSDOC", "%emacs_dir%/etc"},
+      {"TERM", "cmd"}
     };
 
+#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);
+             }
          }
       }
   }
@@ -758,7 +817,7 @@ init_environment ()
 
   {
     char *p;
-    char modname[MAX_PATH];
+    static char modname[MAX_PATH];
 
     if (!GetModuleFileName (NULL, modname, MAX_PATH))
       abort ();
@@ -767,8 +826,17 @@ 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 ();
 }
 
@@ -783,6 +851,7 @@ char *
 get_emacs_configuration (void)
 {
   char *arch, *oem, *os;
+  int build_num;
 
   /* Determine the processor type.  */
   switch (get_processor_type ()) 
@@ -824,10 +893,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;
 }
 
@@ -1097,6 +1193,13 @@ map_w32_filename (const char * name, const char ** pPath)
   char * path;
   const char * save_name = name;
 
+  if (strlen (name) >= MAX_PATH)
+    {
+      /* Return a filename which will cause callers to fail.  */
+      strcpy (shortname, "?");
+      return shortname;
+    }
+
   if (is_fat_volume (name, &path)) /* truncate to 8.3 */
     {
       register int left = 8;   /* maximum number of chars in part */
@@ -1861,7 +1964,7 @@ stat (const char * path, struct stat * buf)
     }
 
   name = (char *) map_w32_filename (path, &path);
-  /* must be valid filename, no wild cards or other illegal characters */
+  /* must be valid filename, no wild cards or other invalid characters */
   if (strpbrk (name, "*?|<>\""))
     {
       errno = ENOENT;
@@ -1921,9 +2024,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)
        {
@@ -1960,19 +2065,6 @@ stat (const char * path, struct stat * buf)
 
       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
@@ -1980,13 +2072,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
     {
@@ -2351,6 +2457,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. */
@@ -2400,35 +2599,51 @@ sys_socket(int af, int type, int protocol)
          _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.  */
          {
-           HANDLE parent;
-           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,
-                                             0))
+           if (pfn_SetHandleInformation)
+             {
+               pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0);
+             }
+           else
              {
-               DuplicateHandle (parent,
-                                (HANDLE) s,
-                                parent,
-                                &new_s,
-                                0,
-                                FALSE,
-                                DUPLICATE_SAME_ACCESS);
-               pfn_closesocket (s);
-               s = (SOCKET) new_s;
+               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)
+                     {
+                       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 */
@@ -3027,14 +3242,19 @@ check_windows_init_file ()
   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 (Vload_path, init_file, ".el:.elc", NULL, 0);
+      fd = openp (full_load_path, init_file, ".el:.elc", NULL, 0);
       if (fd < 0) 
        {
-         Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
+         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);