]> code.delx.au - gnu-emacs/blobdiff - src/w32.c
Fix so top -10 and left -10 in frame parameters work.
[gnu-emacs] / src / w32.c
index c6660c0d3d650555baaf49641cf41afad7b763a2..2ff344abd654e9de998fa3fe10f9aff08a223d40 100644 (file)
--- a/src/w32.c
+++ b/src/w32.c
@@ -1,6 +1,6 @@
 /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API.
    Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-                 2007, 2008  Free Software Foundation, Inc.
+                 2007, 2008, 2009, 2010  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -23,6 +23,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <stddef.h> /* for offsetof */
 #include <stdlib.h>
 #include <stdio.h>
+#include <float.h>     /* for DBL_EPSILON */
 #include <io.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -33,6 +34,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <sys/utime.h>
 #include <mbstring.h>  /* for _mbspbrk */
 #include <math.h>
+#include <setjmp.h>
 
 /* must include CRT headers *before* config.h */
 
@@ -191,6 +193,8 @@ static BOOL g_b_init_global_memory_status_ex;
 static BOOL g_b_init_get_length_sid;
 static BOOL g_b_init_equal_sid;
 static BOOL g_b_init_copy_sid;
+static BOOL g_b_init_get_native_system_info;
+static BOOL g_b_init_get_system_times;
 
 /*
   BEGIN: Wrapper functions around OpenProcessToken
@@ -293,6 +297,12 @@ typedef BOOL (WINAPI * EqualSid_Proc) (
     PSID pSid2);
 typedef DWORD (WINAPI * GetLengthSid_Proc) (
     PSID pSid);
+typedef void (WINAPI * GetNativeSystemInfo_Proc) (
+    LPSYSTEM_INFO lpSystemInfo);
+typedef BOOL (WINAPI * GetSystemTimes_Proc) (
+    LPFILETIME lpIdleTime,
+    LPFILETIME lpKernelTime,
+    LPFILETIME lpUserTime);
 
 
 
@@ -723,6 +733,47 @@ BOOL WINAPI copy_sid (
   supported in Windows NT / 2k / XP
 */
 
+void WINAPI get_native_system_info (
+    LPSYSTEM_INFO lpSystemInfo)
+{
+  static GetNativeSystemInfo_Proc s_pfn_Get_Native_System_Info = NULL;
+  if (is_windows_9x () != TRUE)
+    {
+      if (g_b_init_get_native_system_info == 0)
+       {
+         g_b_init_get_native_system_info = 1;
+         s_pfn_Get_Native_System_Info =
+           (GetNativeSystemInfo_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                                     "GetNativeSystemInfo");
+       }
+      if (s_pfn_Get_Native_System_Info != NULL)
+       s_pfn_Get_Native_System_Info (lpSystemInfo);
+    }
+  else
+    lpSystemInfo->dwNumberOfProcessors = -1;
+}
+
+BOOL WINAPI get_system_times(
+    LPFILETIME lpIdleTime,
+    LPFILETIME lpKernelTime,
+    LPFILETIME lpUserTime)
+{
+  static GetSystemTimes_Proc s_pfn_Get_System_times = NULL;
+  if (is_windows_9x () == TRUE)
+    {
+      return FALSE;
+    }
+  if (g_b_init_get_system_times == 0)
+    {
+      g_b_init_get_system_times = 1;
+      s_pfn_Get_System_times =
+       (GetSystemTimes_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+                                            "GetSystemTimes");
+    }
+  if (s_pfn_Get_System_times == NULL)
+    return FALSE;
+  return (s_pfn_Get_System_times (lpIdleTime, lpKernelTime, lpUserTime));
+}
 \f
 /* Equivalent of strerror for W32 error codes.  */
 char *
@@ -795,17 +846,160 @@ gethostname (char *buffer, int size)
 #endif /* HAVE_SOCKETS */
 
 /* Emulate getloadavg.  */
+
+struct load_sample {
+  time_t sample_time;
+  ULONGLONG idle;
+  ULONGLONG kernel;
+  ULONGLONG user;
+};
+
+/* Number of processors on this machine.  */
+static unsigned num_of_processors;
+
+/* We maintain 1-sec samples for the last 16 minutes in a circular buffer.  */
+static struct load_sample samples[16*60];
+static int first_idx = -1, last_idx = -1;
+static int max_idx = sizeof (samples) / sizeof (samples[0]);
+
+static int
+buf_next (int from)
+{
+  int next_idx = from + 1;
+
+  if (next_idx >= max_idx)
+    next_idx = 0;
+
+  return next_idx;
+}
+
+static int
+buf_prev (int from)
+{
+  int prev_idx = from - 1;
+
+  if (prev_idx < 0)
+    prev_idx = max_idx - 1;
+
+  return prev_idx;
+}
+
+static void
+sample_system_load (ULONGLONG *idle, ULONGLONG *kernel, ULONGLONG *user)
+{
+  SYSTEM_INFO sysinfo;
+  FILETIME ft_idle, ft_user, ft_kernel;
+
+  /* Initialize the number of processors on this machine.  */
+  if (num_of_processors <= 0)
+    {
+      get_native_system_info (&sysinfo);
+      num_of_processors = sysinfo.dwNumberOfProcessors;
+      if (num_of_processors <= 0)
+       {
+         GetSystemInfo (&sysinfo);
+         num_of_processors = sysinfo.dwNumberOfProcessors;
+       }
+      if (num_of_processors <= 0)
+       num_of_processors = 1;
+    }
+
+  /* TODO: Take into account threads that are ready to run, by
+     sampling the "\System\Processor Queue Length" performance
+     counter.  The code below accounts only for threads that are
+     actually running.  */
+
+  if (get_system_times (&ft_idle, &ft_kernel, &ft_user))
+    {
+      ULARGE_INTEGER uidle, ukernel, uuser;
+
+      memcpy (&uidle, &ft_idle, sizeof (ft_idle));
+      memcpy (&ukernel, &ft_kernel, sizeof (ft_kernel));
+      memcpy (&uuser, &ft_user, sizeof (ft_user));
+      *idle = uidle.QuadPart;
+      *kernel = ukernel.QuadPart;
+      *user = uuser.QuadPart;
+    }
+  else
+    {
+      *idle = 0;
+      *kernel = 0;
+      *user = 0;
+    }
+}
+
+/* Produce the load average for a given time interval, using the
+   samples in the samples[] array.  WHICH can be 0, 1, or 2, meaning
+   1-minute, 5-minute, or 15-minute average, respectively. */
+static double
+getavg (int which)
+{
+  double retval = -1.0;
+  double tdiff;
+  int idx;
+  double span = (which == 0 ? 1.0 : (which == 1 ? 5.0 : 15.0)) * 60;
+  time_t now = samples[last_idx].sample_time;
+
+  if (first_idx != last_idx)
+    {
+      for (idx = buf_prev (last_idx); ; idx = buf_prev (idx))
+       {
+         tdiff = difftime (now, samples[idx].sample_time);
+         if (tdiff >= span - 2*DBL_EPSILON*now)
+           {
+             long double sys =
+               samples[last_idx].kernel + samples[last_idx].user
+               - (samples[idx].kernel + samples[idx].user);
+             long double idl = samples[last_idx].idle - samples[idx].idle;
+
+             retval = (1.0 - idl / sys) * num_of_processors;
+             break;
+           }
+         if (idx == first_idx)
+           break;
+       }
+    }
+
+  return retval;
+}
+
 int
 getloadavg (double loadavg[], int nelem)
 {
-  int i;
+  int elem;
+  ULONGLONG idle, kernel, user;
+  time_t now = time (NULL);
 
-  /* A faithful emulation is going to have to be saved for a rainy day.  */
-  for (i = 0; i < nelem; i++)
+  /* Store another sample.  We ignore samples that are less than 1 sec
+     apart.  */
+  if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
     {
-      loadavg[i] = 0.0;
+      sample_system_load (&idle, &kernel, &user);
+      last_idx = buf_next (last_idx);
+      samples[last_idx].sample_time = now;
+      samples[last_idx].idle = idle;
+      samples[last_idx].kernel = kernel;
+      samples[last_idx].user = user;
+      /* If the buffer has more that 15 min worth of samples, discard
+        the old ones.  */
+      if (first_idx == -1)
+       first_idx = last_idx;
+      while (first_idx != last_idx
+            && (difftime (now, samples[first_idx].sample_time)
+                >= 15.0*60 + 2*DBL_EPSILON*now))
+       first_idx = buf_next (first_idx);
+    }
+
+  for (elem = 0; elem < nelem; elem++)
+    {
+      double avg = getavg (elem);
+
+      if (avg < 0)
+       break;
+      loadavg[elem] = avg;
     }
-  return i;
+
+  return elem;
 }
 
 /* Emulate getpwuid, getpwnam and others.  */
@@ -840,13 +1034,13 @@ static struct group dflt_group =
   0,
 };
 
-int
+unsigned
 getuid ()
 {
   return dflt_passwd.pw_uid;
 }
 
-int
+unsigned
 geteuid ()
 {
   /* I could imagine arguing for checking to see whether the user is
@@ -855,20 +1049,20 @@ geteuid ()
   return getuid ();
 }
 
-int
+unsigned
 getgid ()
 {
   return dflt_passwd.pw_gid;
 }
 
-int
+unsigned
 getegid ()
 {
   return getgid ();
 }
 
 struct passwd *
-getpwuid (int uid)
+getpwuid (unsigned uid)
 {
   if (uid == dflt_passwd.pw_uid)
     return &dflt_passwd;
@@ -2362,12 +2556,23 @@ logon_network_drive (const char *path)
   char share[MAX_PATH];
   int i, n_slashes;
   char drive[4];
+  UINT drvtype;
 
-  sprintf (drive, "%c:\\", path[0]);
+  if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
+    drvtype = DRIVE_REMOTE;
+  else if (path[0] == '\0' || path[1] != ':')
+    drvtype = GetDriveType (NULL);
+  else
+    {
+      drive[0] = path[0];
+      drive[1] = ':';
+      drive[2] = '\\';
+      drive[3] = '\0';
+      drvtype = GetDriveType (drive);
+    }
 
   /* Only logon to networked drives.  */
-  if ((!IS_DIRECTORY_SEP (path[0]) || !IS_DIRECTORY_SEP (path[1]))
-      && GetDriveType (drive) != DRIVE_REMOTE)
+  if (drvtype != DRIVE_REMOTE)
     return;
 
   n_slashes = 2;
@@ -2739,76 +2944,69 @@ sys_unlink (const char * path)
 }
 
 static FILETIME utc_base_ft;
-static long double utc_base;
+static ULONGLONG utc_base;  /* In 100ns units */
 static int init = 0;
 
-static long double
-convert_time_raw (FILETIME ft)
+#define FILETIME_TO_U64(result, ft)        \
+  do {                                     \
+    ULARGE_INTEGER uiTemp;                 \
+    uiTemp.LowPart = (ft).dwLowDateTime;   \
+    uiTemp.HighPart = (ft).dwHighDateTime; \
+    result = uiTemp.QuadPart;              \
+  } while (0)
+
+static void
+initialize_utc_base ()
 {
-  return
-    (long double) ft.dwHighDateTime
-    * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime;
+  /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
+  SYSTEMTIME st;
+
+  st.wYear = 1970;
+  st.wMonth = 1;
+  st.wDay = 1;
+  st.wHour = 0;
+  st.wMinute = 0;
+  st.wSecond = 0;
+  st.wMilliseconds = 0;
+
+  SystemTimeToFileTime (&st, &utc_base_ft);
+  FILETIME_TO_U64 (utc_base, utc_base_ft);
 }
 
 static time_t
 convert_time (FILETIME ft)
 {
-  long double ret;
+  ULONGLONG tmp;
 
   if (!init)
     {
-      /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
-      SYSTEMTIME st;
-
-      st.wYear = 1970;
-      st.wMonth = 1;
-      st.wDay = 1;
-      st.wHour = 0;
-      st.wMinute = 0;
-      st.wSecond = 0;
-      st.wMilliseconds = 0;
-
-      SystemTimeToFileTime (&st, &utc_base_ft);
-      utc_base = (long double) utc_base_ft.dwHighDateTime
-       * 4096.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime;
+      initialize_utc_base();
       init = 1;
     }
 
   if (CompareFileTime (&ft, &utc_base_ft) < 0)
     return 0;
 
-  return (time_t) ((convert_time_raw (ft) - utc_base) * 1e-7L);
+  FILETIME_TO_U64 (tmp, ft);
+  return (time_t) ((tmp - utc_base) / 10000000L);
 }
 
 
 void
 convert_from_time_t (time_t time, FILETIME * pft)
 {
-  long double tmp;
+  ULARGE_INTEGER tmp;
 
   if (!init)
     {
-      /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */
-      SYSTEMTIME st;
-
-      st.wYear = 1970;
-      st.wMonth = 1;
-      st.wDay = 1;
-      st.wHour = 0;
-      st.wMinute = 0;
-      st.wSecond = 0;
-      st.wMilliseconds = 0;
-
-      SystemTimeToFileTime (&st, &utc_base_ft);
-      utc_base = (long double) utc_base_ft.dwHighDateTime
-       * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime;
+      initialize_utc_base ();
       init = 1;
     }
 
   /* time in 100ns units since 1-Jan-1601 */
-  tmp = (long double) time * 1e7 + utc_base;
-  pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024));
-  pft->dwLowDateTime = (DWORD) (tmp - (4096.0 * 1024 * 1024) * pft->dwHighDateTime);
+  tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base;
+  pft->dwHighDateTime = tmp.HighPart;
+  pft->dwLowDateTime = tmp.LowPart;
 }
 
 #if 0
@@ -2901,7 +3099,7 @@ get_rid (PSID sid)
 #endif
 
 struct w32_id {
-  int rid;
+  unsigned rid;
   struct w32_id *next;
   char name[GNLEN+1];
   unsigned char sid[FLEXIBLE_ARRAY_MEMBER];
@@ -2910,7 +3108,7 @@ struct w32_id {
 static struct w32_id *w32_idlist;
 
 static int
-w32_cached_id (PSID sid, int *id, char *name)
+w32_cached_id (PSID sid, unsigned *id, char *name)
 {
   struct w32_id *tail, *found;
 
@@ -2933,7 +3131,7 @@ w32_cached_id (PSID sid, int *id, char *name)
 }
 
 static void
-w32_add_to_cache (PSID sid, int id, char *name)
+w32_add_to_cache (PSID sid, unsigned id, char *name)
 {
   DWORD sid_len;
   struct w32_id *new_entry;
@@ -2960,7 +3158,7 @@ w32_add_to_cache (PSID sid, int id, char *name)
 
 static int
 get_name_and_id (PSECURITY_DESCRIPTOR psd, const char *fname,
-                int *id, char *nm, int what)
+                unsigned *id, char *nm, int what)
 {
   PSID sid = NULL;
   char machine[MAX_COMPUTERNAME_LENGTH+1];
@@ -3048,6 +3246,28 @@ get_file_owner_and_group (
     }
 }
 
+/* Return non-zero if NAME is a potentially slow filesystem.  */
+int
+is_slow_fs (const char *name)
+{
+  char drive_root[4];
+  UINT devtype;
+
+  if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
+    devtype = DRIVE_REMOTE;       /* assume UNC name is remote */
+  else if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
+    devtype = GetDriveType (NULL); /* use root of current drive */
+  else
+    {
+      /* GetDriveType needs the root directory of the drive.  */
+      strncpy (drive_root, name, 2);
+      drive_root[2] = '\\';
+      drive_root[3] = '\0';
+      devtype = GetDriveType (drive_root);
+    }
+  return !(devtype == DRIVE_FIXED || devtype == DRIVE_RAMDISK);
+}
+
 /* MSVC stat function can't cope with UNC names and has other bugs, so
    replace it with our own.  This also allows us to calculate consistent
    inode values without hacks in the main Emacs code. */
@@ -3161,19 +3381,8 @@ stat (const char * path, struct stat * buf)
        }
     }
 
-  /* GetDriveType needs the root directory of NAME's drive.  */
-  if (!(strlen (name) >= 2 && IS_DEVICE_SEP (name[1])))
-    devtype = GetDriveType (NULL); /* use root of current diectory */
-  else
-    {
-      strncpy (drive_root, name, 3);
-      drive_root[3] = '\0';
-      devtype = GetDriveType (drive_root);
-    }
-
   if (!(NILP (Vw32_get_true_file_attributes)
-       || (EQ (Vw32_get_true_file_attributes, Qlocal)
-           && devtype != DRIVE_FIXED && devtype != DRIVE_RAMDISK))
+       || (EQ (Vw32_get_true_file_attributes, Qlocal) && is_slow_fs (name)))
       /* No access rights required to get info.  */
       && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
                           FILE_FLAG_BACKUP_SEMANTICS, NULL))
@@ -3671,7 +3880,7 @@ BOOL WINAPI global_memory_status_ex (
 }
 
 Lisp_Object
-w32_list_system_processes ()
+list_system_processes ()
 {
   struct gcpro gcpro1;
   Lisp_Object proclist = Qnil;
@@ -3770,16 +3979,16 @@ ltime (time_sec, time_usec)
                make_number (time_usec));
 }
 
+#define U64_TO_LISP_TIME(time) ltime ((time) / 1000000L, (time) % 1000000L)
+
 static int
-process_times (h_proc, ctime, etime, stime, utime, pcpu)
+process_times (h_proc, ctime, etime, stime, utime, ttime, pcpu)
      HANDLE h_proc;
-     Lisp_Object *ctime, *etime, *stime, *utime;
+     Lisp_Object *ctime, *etime, *stime, *utime, *ttime;
      double *pcpu;
 {
   FILETIME ft_creation, ft_exit, ft_kernel, ft_user, ft_current;
-  long ctime_sec, ctime_usec, stime_sec, stime_usec, utime_sec, utime_usec;
-  long etime_sec, etime_usec;
-  long double tem1, tem2, tem;
+  ULONGLONG tem1, tem2, tem3, tem;
 
   if (!h_proc
       || !get_process_times_fn
@@ -3789,26 +3998,29 @@ process_times (h_proc, ctime, etime, stime, utime, pcpu)
 
   GetSystemTimeAsFileTime (&ft_current);
 
-  tem1 = convert_time_raw (ft_kernel) * 0.1L;
-  stime_usec = fmodl (tem1, 1000000.0L);
-  stime_sec = tem1 * 0.000001L;
-  *stime = ltime (stime_sec, stime_usec);
-  tem2 = convert_time_raw (ft_user) * 0.1L;
-  utime_usec = fmodl (tem2, 1000000.0L);
-  utime_sec = tem2 * 0.000001L;
-  *utime = ltime (utime_sec, utime_usec);
-  tem = convert_time_raw (ft_creation);
+  FILETIME_TO_U64 (tem1, ft_kernel);
+  tem1 /= 10L;
+  *stime = U64_TO_LISP_TIME (tem1);
+
+  FILETIME_TO_U64 (tem2, ft_user);
+  tem2 /= 10L;
+  *utime = U64_TO_LISP_TIME (tem2);
+
+  tem3 = tem1 + tem2;
+  *ttime = U64_TO_LISP_TIME (tem3);
+
+  FILETIME_TO_U64 (tem, ft_creation);
   /* Process no 4 (System) returns zero creation time.  */
   if (tem)
-    tem = (tem - utc_base) * 0.1;
-  ctime_usec = fmodl (tem, 1000000.0L);
-  ctime_sec = tem * 0.000001L;
-  *ctime = ltime (ctime_sec, ctime_usec);
+    tem = (tem - utc_base) / 10L;
+  *ctime = U64_TO_LISP_TIME (tem);
+
   if (tem)
-    tem = (convert_time_raw (ft_current) - utc_base) * 0.1L - tem;
-  etime_usec = fmodl (tem, 1000000.0L);
-  etime_sec = tem * 0.000001L;
-  *etime = ltime (etime_sec, etime_usec);
+    {
+      FILETIME_TO_U64 (tem3, ft_current);
+      tem = (tem3 - utc_base) / 10L - tem;
+    }
+  *etime = U64_TO_LISP_TIME (tem);
 
   if (tem)
     {
@@ -3823,7 +4035,7 @@ process_times (h_proc, ctime, etime, stime, utime, pcpu)
 }
 
 Lisp_Object
-w32_system_process_attributes (pid)
+system_process_attributes (pid)
      Lisp_Object pid;
 {
   struct gcpro gcpro1, gcpro2, gcpro3;
@@ -3841,8 +4053,8 @@ w32_system_process_attributes (pid)
   DWORD blen = 0;
   TOKEN_USER user_token;
   TOKEN_PRIMARY_GROUP group_token;
-  int euid;
-  int egid;
+  unsigned euid;
+  unsigned egid;
   DWORD sess;
   PROCESS_MEMORY_COUNTERS mem;
   PROCESS_MEMORY_COUNTERS_EX mem_ex;
@@ -3850,7 +4062,7 @@ w32_system_process_attributes (pid)
   MEMORYSTATUS memst;
   MEMORY_STATUS_EX memstex;
   double totphys = 0.0;
-  Lisp_Object ctime, stime, utime, etime;
+  Lisp_Object ctime, stime, utime, etime, ttime;
   double pcpu;
   BOOL result = FALSE;
 
@@ -3999,8 +4211,7 @@ w32_system_process_attributes (pid)
                }
            }
        }
-      if (buf)
-       xfree (buf);
+      xfree (buf);
     }
   if (!result)
     {
@@ -4109,10 +4320,11 @@ w32_system_process_attributes (pid)
        attrs = Fcons (Fcons (Qpmem, make_float (100. * rss / totphys)), attrs);
     }
 
-  if (process_times (h_proc, &ctime, &etime, &stime, &utime, &pcpu))
+  if (process_times (h_proc, &ctime, &etime, &stime, &utime, &ttime, &pcpu))
     {
       attrs = Fcons (Fcons (Qutime, utime), attrs);
       attrs = Fcons (Fcons (Qstime, stime), attrs);
+      attrs = Fcons (Fcons (Qtime,  ttime), attrs);
       attrs = Fcons (Fcons (Qstart, ctime), attrs);
       attrs = Fcons (Fcons (Qetime, etime), attrs);
       attrs = Fcons (Fcons (Qpcpu, make_float (pcpu)), attrs);
@@ -5697,6 +5909,9 @@ globals_of_w32 ()
   g_b_init_equal_sid = 0;
   g_b_init_copy_sid = 0;
   g_b_init_get_length_sid = 0;
+  g_b_init_get_native_system_info = 0;
+  g_b_init_get_system_times = 0;
+  num_of_processors = 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