]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
MAINTAINERS: Add myself for ERC and tq.el
[gnu-emacs] / src / w32.c
index c7f6e3172f91b416a278f7b27234e51241a95237..dd87ef62fabd0cbbaaa1ee503f2684bf41a59f4c 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,12 +1,12 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
    Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004,
-                 2005 Free Software Foundation, Inc.
+                 2005, 2006, 2007 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
@@ -32,6 +32,7 @@ Boston, MA 02110-1301, USA.
 #include <sys/file.h>
 #include <sys/time.h>
 #include <sys/utime.h>
+#include <mbstring.h>  /* for _mbspbrk */
 
 /* must include CRT headers *before* config.h */
 
@@ -112,7 +113,7 @@ extern int w32_num_mouse_buttons;
 
 \f
 /*
-       Initialization states
+  Initialization states
  */
 static BOOL g_b_init_is_windows_9x;
 static BOOL g_b_init_open_process_token;
@@ -136,6 +137,15 @@ typedef BOOL (WINAPI * GetTokenInformation_Proc) (
     LPVOID TokenInformation,
     DWORD TokenInformationLength,
     PDWORD ReturnLength);
+typedef BOOL (WINAPI * GetProcessTimes_Proc) (
+    HANDLE process_handle,
+    LPFILETIME creation_time,
+    LPFILETIME exit_time,
+    LPFILETIME kernel_time,
+    LPFILETIME user_time);
+
+GetProcessTimes_Proc get_process_times_fn = NULL;
+
 #ifdef _UNICODE
 const char * const LookupAccountSid_Name = "LookupAccountSidW";
 #else
@@ -153,7 +163,8 @@ typedef PSID_IDENTIFIER_AUTHORITY (WINAPI * GetSidIdentifierAuthority_Proc) (
     PSID pSid);
 
   /* ** A utility function ** */
-static BOOL is_windows_9x ()
+static BOOL
+is_windows_9x ()
 {
   static BOOL s_b_ret=0;
   OSVERSIONINFO os_ver;
@@ -170,6 +181,46 @@ static BOOL is_windows_9x ()
   return s_b_ret;
 }
 
+/* Get total user and system times for get-internal-run-time.
+   Returns a list of three integers if the times are provided by the OS
+   (NT derivatives), otherwise it returns the result of current-time. */
+Lisp_Object
+w32_get_internal_run_time ()
+{
+  if (get_process_times_fn)
+    {
+      FILETIME create, exit, kernel, user;
+      HANDLE proc = GetCurrentProcess();
+      if ((*get_process_times_fn) (proc, &create, &exit, &kernel, &user))
+        {
+          LARGE_INTEGER user_int, kernel_int, total;
+          int microseconds;
+          user_int.LowPart = user.dwLowDateTime;
+          user_int.HighPart = user.dwHighDateTime;
+          kernel_int.LowPart = kernel.dwLowDateTime;
+          kernel_int.HighPart = kernel.dwHighDateTime;
+          total.QuadPart = user_int.QuadPart + kernel_int.QuadPart;
+          /* FILETIME is 100 nanosecond increments, Emacs only wants
+             microsecond resolution.  */
+          total.QuadPart /= 10;
+          microseconds = total.QuadPart % 1000000;
+          total.QuadPart /= 1000000;
+
+          /* Sanity check to make sure we can represent the result.  */
+          if (total.HighPart == 0)
+            {
+              int secs = total.LowPart;
+
+              return list3 (make_number ((secs >> 16) & 0xffff),
+                            make_number (secs & 0xffff),
+                            make_number (microseconds));
+            }
+        }
+    }
+
+  return Fcurrent_time ();
+}
+
   /* ** The wrapper functions ** */
 
 BOOL WINAPI open_process_token (
@@ -323,6 +374,28 @@ w32_strerror (int error_no)
   return buf;
 }
 
+/* Return 1 if P is a valid pointer to an object of size SIZE.  Return
+   0 if P is NOT a valid pointer.  Return -1 if we cannot validate P.
+
+   This is called from alloc.c:valid_pointer_p.  */
+int
+w32_valid_pointer_p (void *p, int size)
+{
+  SIZE_T done;
+  HANDLE h = OpenProcess (PROCESS_VM_READ, FALSE, GetCurrentProcessId ());
+
+  if (h)
+    {
+      unsigned char *buf = alloca (size);
+      int retval = ReadProcessMemory (h, p, buf, size, &done);
+
+      CloseHandle (h);
+      return retval;
+    }
+  else
+    return -1;
+}
+
 static char startup_dir[MAXPATHLEN];
 
 /* Get the current working directory.  */
@@ -462,20 +535,16 @@ init_user_info ()
      the user-sid as the user id value (same for group id using the
      primary group sid from the process token). */
 
-  char            user_sid[256], name[256], domain[256];
-  DWORD           length = sizeof (name), dlength = sizeof (domain), trash;
-  HANDLE          token = NULL;
-  SID_NAME_USE    user_type;
-
-  if (
-                       open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
-      && get_token_information (
-                                       token, TokenUser,
-                             (PVOID) user_sid, sizeof (user_sid), &trash)
-      && lookup_account_sid (
-                                       NULL, *((PSID *) user_sid), name, &length,
-                          domain, &dlength, &user_type)
-                       )
+  char         user_sid[256], name[256], domain[256];
+  DWORD        length = sizeof (name), dlength = sizeof (domain), trash;
+  HANDLE       token = NULL;
+  SID_NAME_USE user_type;
+
+  if (open_process_token (GetCurrentProcess (), TOKEN_QUERY, &token)
+      && get_token_information (token, TokenUser,
+                               (PVOID) user_sid, sizeof (user_sid), &trash)
+      && lookup_account_sid (NULL, *((PSID *) user_sid), name, &length,
+                            domain, &dlength, &user_type))
     {
       strcpy (the_passwd.pw_name, name);
       /* Determine a reasonable uid value. */
@@ -500,7 +569,7 @@ init_user_info ()
 
          /* Get group id */
          if (get_token_information (token, TokenPrimaryGroup,
-                                  (PVOID) user_sid, sizeof (user_sid), &trash))
+                                    (PVOID) user_sid, sizeof (user_sid), &trash))
            {
              SID_IDENTIFIER_AUTHORITY * pSIA;
 
@@ -517,7 +586,7 @@ init_user_info ()
        }
     }
   /* If security calls are not supported (presumably because we
-       are running under Windows 95), fallback to this. */
+     are running under Windows 95), fallback to this. */
   else if (GetUserName (name, &length))
     {
       strcpy (the_passwd.pw_name, name);
@@ -716,7 +785,7 @@ get_long_basename (char * name, char * buf, int size)
   int len = 0;
 
   /* must be valid filename, no wild cards or other invalid characters */
-  if (strpbrk (name, "*?|<>\""))
+  if (_mbspbrk (name, "*?|<>\""))
     return 0;
 
   dir_handle = FindFirstFile (name, &find_data);
@@ -791,7 +860,7 @@ is_unc_volume (const char *filename)
   if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2])
     return 0;
 
-  if (strpbrk (ptr + 2, "*?|<>\"\\/"))
+  if (_mbspbrk (ptr + 2, "*?|<>\"\\/"))
     return 0;
 
   return 1;
@@ -835,18 +904,6 @@ alarm (int seconds)
   return 0;
 }
 
-void
-unrequest_sigio (void)
-{
-  return;
-}
-
-void
-request_sigio (void)
-{
-  return;
-}
-
 #define REG_ROOT "SOFTWARE\\GNU\\Emacs"
 
 LPBYTE
@@ -950,11 +1007,11 @@ init_environment (char ** argv)
     struct stat ignored;
     char default_home[MAX_PATH];
 
-    static struct env_entry
+    static const struct env_entry
     {
       char * name;
       char * def_value;
-    } env_vars[] =
+    } dflt_envvars[] =
     {
       {"HOME", "C:/"},
       {"PRELOAD_WINSOCK", NULL},
@@ -971,6 +1028,17 @@ init_environment (char ** argv)
       {"LANG", NULL},
     };
 
+#define N_ENV_VARS sizeof(dflt_envvars)/sizeof(dflt_envvars[0])
+
+    /* We need to copy dflt_envvars[] and work on the copy because we
+       don't want the dumped Emacs to inherit the values of
+       environment variables we saw during dumping (which could be on
+       a different system).  The defaults above must be left intact.  */
+    struct env_entry env_vars[N_ENV_VARS];
+
+    for (i = 0; i < N_ENV_VARS; i++)
+      env_vars[i] = dflt_envvars[i];
+
     /* For backwards compatibility, check if a .emacs file exists in C:/
        If not, then we can try to default to the appdata directory under the
        user's profile, which is more likely to be writable.   */
@@ -1005,7 +1073,7 @@ init_environment (char ** argv)
                      LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
                      locale_name, sizeof (locale_name)))
     {
-      for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
+      for (i = 0; i < N_ENV_VARS; i++)
         {
           if (strcmp (env_vars[i].name, "LANG") == 0)
             {
@@ -1069,13 +1137,15 @@ init_environment (char ** argv)
        }
     }
 
-    for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++)
+    for (i = 0; i < N_ENV_VARS; i++)
       {
        if (!getenv (env_vars[i].name))
          {
            int dont_free = 0;
 
-           if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL)
+           if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL
+               /* Also ignore empty environment variables.  */
+               || *lpval == 0)
              {
                lpval = env_vars[i].def_value;
                dwType = REG_EXPAND_SZ;
@@ -1084,20 +1154,17 @@ init_environment (char ** argv)
 
            if (lpval)
              {
-               if (dwType == REG_EXPAND_SZ)
-                 {
-                   char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE];
+               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));
-                 }
+               if (dwType == REG_EXPAND_SZ)
+                 ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof(buf1));
                else if (dwType == REG_SZ)
+                 strcpy (buf1, lpval);
+               if (dwType == REG_EXPAND_SZ || dwType == REG_SZ)
                  {
-                   char buf[SET_ENV_BUF_SIZE];
-
-                   _snprintf (buf, sizeof(buf)-1, "%s=%s", env_vars[i].name, lpval);
-                   _putenv (strdup (buf));
+                   _snprintf (buf2, sizeof(buf2)-1, "%s=%s", env_vars[i].name,
+                              buf1);
+                   _putenv (strdup (buf2));
                  }
 
                if (!dont_free)
@@ -2248,16 +2315,17 @@ convert_time (FILETIME ft)
 
       SystemTimeToFileTime (&st, &utc_base_ft);
       utc_base = (long double) utc_base_ft.dwHighDateTime
-       * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
+       * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
       init = 1;
     }
 
   if (CompareFileTime (&ft, &utc_base_ft) < 0)
     return 0;
 
-  ret = (long double) ft.dwHighDateTime * 4096 * 1024 * 1024 + ft.dwLowDateTime;
+  ret = (long double) ft.dwHighDateTime
+    * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
   ret -= utc_base;
-  return (time_t) (ret * 1e-7);
+  return (time_t) (ret * 1e-7L);
 }
 
 void
@@ -2354,8 +2422,12 @@ stat (const char * path, struct stat * buf)
     }
 
   name = (char *) map_w32_filename (path, &path);
-  /* must be valid filename, no wild cards or other invalid characters */
-  if (strpbrk (name, "*?|<>\""))
+  /* Must be valid filename, no wild cards or other invalid
+     characters.  We use _mbspbrk to support multibyte strings that
+     might look to strpbrk as if they included literal *, ?, and other
+     characters mentioned below that are disallowed by Windows
+     filesystems.  */
+  if (_mbspbrk (name, "*?|<>\""))
     {
       errno = ENOENT;
       return -1;
@@ -2444,7 +2516,7 @@ stat (const char * path, struct stat * buf)
          != 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
+        of links, but is quite slow (it is noticeable when Emacs is
         making a list of file name completions). */
       BY_HANDLE_FILE_INFORMATION info;
 
@@ -2692,6 +2764,9 @@ utime (const char *name, struct utimbuf *times)
 int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData);
 void (PASCAL *pfn_WSASetLastError) (int iError);
 int (PASCAL *pfn_WSAGetLastError) (void);
+int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents);
+HANDLE (PASCAL *pfn_WSACreateEvent) (void);
+int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent);
 int (PASCAL *pfn_socket) (int af, int type, int protocol);
 int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen);
 int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen);
@@ -2761,7 +2836,7 @@ init_winsock (int load_now)
     = (void *) GetProcAddress (GetModuleHandle ("kernel32.dll"),
                               "SetHandleInformation");
 
-  winsock_lib = LoadLibrary ("wsock32.dll");
+  winsock_lib = LoadLibrary ("Ws2_32.dll");
 
   if (winsock_lib != NULL)
     {
@@ -2774,6 +2849,9 @@ init_winsock (int load_now)
       LOAD_PROC( WSAStartup );
       LOAD_PROC( WSASetLastError );
       LOAD_PROC( WSAGetLastError );
+      LOAD_PROC( WSAEventSelect );
+      LOAD_PROC( WSACreateEvent );
+      LOAD_PROC( WSACloseEvent );
       LOAD_PROC( socket );
       LOAD_PROC( bind );
       LOAD_PROC( connect );
@@ -2834,7 +2912,8 @@ int h_errno = 0;
 /* function to set h_errno for compatability; map winsock error codes to
    normal system codes where they overlap (non-overlapping definitions
    are already in <sys/socket.h> */
-static void set_errno ()
+static void
+set_errno ()
 {
   if (winsock_lib == NULL)
     h_errno = EINVAL;
@@ -2855,7 +2934,8 @@ static void set_errno ()
   errno = h_errno;
 }
 
-static void check_errno ()
+static void
+check_errno ()
 {
   if (h_errno == 0 && winsock_lib != NULL)
     pfn_WSASetLastError (0);
@@ -2921,7 +3001,7 @@ struct {
   WSAEINVALIDPROCTABLE    , "Invalid procedure table from service provider",
   WSAEINVALIDPROVIDER     , "Invalid service provider version number",
   WSAEPROVIDERFAILEDINIT  , "Unable to initialize a service provider",
-  WSASYSCALLFAILURE       , "System call failured",
+  WSASYSCALLFAILURE       , "System call failure",
   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 */
@@ -3287,6 +3367,8 @@ sys_listen (int s, int backlog)
       int rc = pfn_listen (SOCK_HANDLE (s), backlog);
       if (rc == SOCKET_ERROR)
        set_errno ();
+      else
+       fd_info[s].flags |= FILE_LISTEN;
       return rc;
     }
   h_errno = ENOTSOCK;
@@ -3324,14 +3406,18 @@ sys_accept (int s, struct sockaddr * addr, int * addrlen)
     }
 
   check_errno ();
-  if (fd_info[s].flags & FILE_SOCKET)
+  if (fd_info[s].flags & FILE_LISTEN)
     {
       SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen);
-      if (t != INVALID_SOCKET)
-       return socket_to_fd (t);
+      int fd = -1;
+      if (t == INVALID_SOCKET)
+       set_errno ();
+      else
+       fd = socket_to_fd (t);
 
-      set_errno ();
-      return -1;
+      fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
+      ResetEvent (fd_info[s].cp->char_avail);
+      return fd;
     }
   h_errno = ENOTSOCK;
   return -1;
@@ -3426,13 +3512,13 @@ sys_close (int fd)
 {
   int rc;
 
-  if (fd < 0 || fd >= MAXDESC)
+  if (fd < 0)
     {
       errno = EBADF;
       return -1;
     }
 
-  if (fd_info[fd].cp)
+  if (fd < MAXDESC && fd_info[fd].cp)
     {
       child_process * cp = fd_info[fd].cp;
 
@@ -3474,7 +3560,7 @@ sys_close (int fd)
      because socket handles are fully fledged kernel handles. */
   rc = _close (fd);
 
-  if (rc == 0)
+  if (rc == 0 && fd < MAXDESC)
     fd_info[fd].flags = 0;
 
   return rc;
@@ -3486,7 +3572,7 @@ sys_dup (int fd)
   int new_fd;
 
   new_fd = _dup (fd);
-  if (new_fd >= 0)
+  if (new_fd >= 0 && new_fd < MAXDESC)
     {
       /* duplicate our internal info as well */
       fd_info[new_fd] = fd_info[fd];
@@ -3633,6 +3719,37 @@ _sys_read_ahead (int fd)
   return cp->status;
 }
 
+int
+_sys_wait_accept (int fd)
+{
+  HANDLE hEv;
+  child_process * cp;
+  int rc;
+
+  if (fd < 0 || fd >= MAXDESC)
+    return STATUS_READ_ERROR;
+
+  cp = fd_info[fd].cp;
+
+  if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
+    return STATUS_READ_ERROR;
+
+  cp->status = STATUS_READ_FAILED;
+
+  hEv = pfn_WSACreateEvent ();
+  rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT);
+  if (rc != SOCKET_ERROR)
+    {
+      rc = WaitForSingleObject (hEv, INFINITE);
+      pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0);
+      if (rc == WAIT_OBJECT_0)
+       cp->status = STATUS_READ_SUCCEEDED;
+    }
+  pfn_WSACloseEvent (hEv);
+
+  return cp->status;
+}
+
 int
 sys_read (int fd, char * buffer, unsigned int count)
 {
@@ -3641,13 +3758,13 @@ sys_read (int fd, char * buffer, unsigned int count)
   DWORD waiting;
   char * orig_buffer = buffer;
 
-  if (fd < 0 || fd >= MAXDESC)
+  if (fd < 0)
     {
       errno = EBADF;
       return -1;
     }
 
-  if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
     {
       child_process *cp = fd_info[fd].cp;
 
@@ -3785,13 +3902,13 @@ sys_write (int fd, const void * buffer, unsigned int count)
 {
   int nchars;
 
-  if (fd < 0 || fd >= MAXDESC)
+  if (fd < 0)
     {
       errno = EBADF;
       return -1;
     }
 
-  if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
+  if (fd < MAXDESC && fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET))
     {
       if ((fd_info[fd].flags & FILE_WRITE) == 0)
        {
@@ -3833,7 +3950,7 @@ sys_write (int fd, const void * buffer, unsigned int count)
     }
 
 #ifdef HAVE_SOCKETS
-  if (fd_info[fd].flags & FILE_SOCKET)
+  if (fd < MAXDESC && fd_info[fd].flags & FILE_SOCKET)
     {
       unsigned long nblock = 0;
       if (winsock_lib == NULL) abort ();
@@ -3888,13 +4005,15 @@ 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, Vload_suffixes, NULL, Qnil);
+      fd = openp (full_load_path, init_file, Fget_load_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);
+         char *buffer = alloca (1024
+                                + strlen (init_file_name)
+                                + strlen (load_path));
 
          sprintf (buffer,
                   "The Emacs Windows initialization file \"%s.el\" "
@@ -4037,21 +4156,51 @@ init_ntproc ()
   check_windows_init_file ();
 }
 
+/*
+        shutdown_handler ensures that buffers' autosave files are
+       up to date when the user logs off, or the system shuts down.
+*/
+BOOL WINAPI shutdown_handler(DWORD type)
+{
+  /* Ctrl-C and Ctrl-Break are already suppressed, so don't handle them.  */
+  if (type == CTRL_CLOSE_EVENT        /* User closes console window.  */
+      || type == CTRL_LOGOFF_EVENT    /* User logs off.  */
+      || type == CTRL_SHUTDOWN_EVENT) /* User shutsdown.  */
+    {
+      /* Shut down cleanly, making sure autosave files are up to date.  */
+      shut_down_emacs (0, 0, Qnil);
+    }
+
+  /* Allow other handlers to handle this signal.  */
+  return FALSE;
+}
+
 /*
        globals_of_w32 is used to initialize those global variables that
        must always be initialized on startup even when the global variable
        initialized is non zero (see the function main in emacs.c).
 */
-void globals_of_w32 ()
+void
+globals_of_w32 ()
 {
+  HMODULE kernel32 = GetModuleHandle ("kernel32.dll");
+
+  get_process_times_fn = (GetProcessTimes_Proc)
+    GetProcAddress (kernel32, "GetProcessTimes");
+
   g_b_init_is_windows_9x = 0;
   g_b_init_open_process_token = 0;
   g_b_init_get_token_information = 0;
   g_b_init_lookup_account_sid = 0;
   g_b_init_get_sid_identifier_authority = 0;
+  /* The following sets a handler for shutdown notifications for
+     console apps. This actually applies to Emacs in both console and
+     GUI modes, since we had to fool windows into thinking emacs is a
+     console application to get console mode to work.  */
+  SetConsoleCtrlHandler(shutdown_handler, TRUE);
 }
 
-/* end of nt.c */
+/* end of w32.c */
 
 /* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1
    (do not change this comment) */