Switch from NO_RETURN to C11's _Noreturn.
[bpt/emacs.git] / src / sysdep.c
index d2ce5a8..9994f31 100644 (file)
@@ -1,5 +1,5 @@
 /* Interfaces to system-dependent kernel and library entries.
-   Copyright (C) 1985-1988, 1993-1995, 1999-2011
+   Copyright (C) 1985-1988, 1993-1995, 1999-2012
                  Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -32,18 +32,27 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <allocator.h>
 #include <careadlinkat.h>
 #include <ignore-value.h>
+#include <utimens.h>
 
 #include "lisp.h"
 #include "sysselect.h"
 #include "blockinput.h"
 
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/resource.h>
+#include <math.h>
+#endif
+
+#ifdef DARWIN_OS
+#include <sys/sysctl.h>
+#endif
+
 #ifdef WINDOWSNT
 #define read sys_read
 #define write sys_write
 #include <windows.h>
-#ifndef NULL
-#define NULL 0
-#endif
 #endif /* not WINDOWSNT */
 
 #include <sys/types.h>
@@ -101,25 +110,11 @@ extern char *getwd (char *);
 
 #include "syssignal.h"
 #include "systime.h"
-#ifdef HAVE_UTIME_H
-#include <utime.h>
-#endif
-
-#ifndef HAVE_UTIMES
-#ifndef HAVE_STRUCT_UTIMBUF
-/* We want to use utime rather than utimes, but we couldn't find the
-   structure declaration.  We'll use the traditional one.  */
-struct utimbuf {
-  long actime;
-  long modtime;
-};
-#endif
-#endif
 
 static int emacs_get_tty (int, struct emacs_tty *);
 static int emacs_set_tty (int, struct emacs_tty *, int);
 #if defined TIOCNOTTY || defined USG5 || defined CYGWIN
-static void croak (char *) NO_RETURN;
+static _Noreturn void croak (char *);
 #endif
 
 /* Declare here, including term.h is problematic on some systems.  */
@@ -433,7 +428,7 @@ child_setup_tty (int out)
 #endif /* AIX */
 
   /* We originally enabled ICANON (and set VEOF to 04), and then had
-     proces.c send additional EOF chars to flush the output when faced
+     process.c send additional EOF chars to flush the output when faced
      with long lines, but this leads to weird effects when the
      subprocess has disabled ICANON and ends up seeing those spurious
      extra EOFs.  So we don't send EOFs any more in
@@ -854,6 +849,7 @@ void
 init_sys_modes (struct tty_display_info *tty_out)
 {
   struct emacs_tty tty;
+  Lisp_Object terminal;
 
   Vtty_erase_char = Qnil;
 
@@ -907,7 +903,9 @@ init_sys_modes (struct tty_display_info *tty_out)
       tty.main.c_cflag &= ~PARENB;/* Don't check parity */
     }
 #endif
-  if (tty_out->input == stdin)
+
+  XSETTERMINAL(terminal, tty_out->terminal);
+  if (!NILP (Fcontrolling_tty_p (terminal)))
     {
       tty.main.c_cc[VINTR] = quit_char;        /* C-g (usually) gives SIGINT */
       /* Set up C-g for both SIGQUIT and SIGINT.
@@ -1660,7 +1658,7 @@ init_signals (void)
       sys_siglist[SIGQUIT] = "Quit";
 # endif
 # ifdef SIGRETRACT
-      sys_siglist[SIGRETRACT] = "Need to relinguish monitor mode";
+      sys_siglist[SIGRETRACT] = "Need to relinquish monitor mode";
 # endif
 # ifdef SIGSAK
       sys_siglist[SIGSAK] = "Secure attention";
@@ -2055,30 +2053,6 @@ perror (void)
 }
 #endif /* HPUX and not HAVE_PERROR */
 
-/*
- *     Gettimeofday.  Simulate as much as possible.  Only accurate
- *     to nearest second.  Emacs doesn't use tzp so ignore it for now.
- *     Only needed when subprocesses are defined.
- */
-
-#ifndef HAVE_GETTIMEOFDAY
-#ifdef HAVE_TIMEVAL
-
-int
-gettimeofday (struct timeval *tp, struct timezone *tzp)
-{
-  extern long time (long);
-
-  tp->tv_sec = time ((long *)0);
-  tp->tv_usec = 0;
-  if (tzp != 0)
-    tzp->tz_minuteswest = -1;
-  return 0;
-}
-
-#endif
-#endif /* !HAVE_GETTIMEOFDAY && HAVE_TIMEVAL */
-
 /*
  *     This function will go away as soon as all the stubs fixed. (fnf)
  */
@@ -2115,20 +2089,43 @@ closedir (DIR *dirp /* stream from opendir */)
 #endif /* HAVE_DIRENT_H */
 
 \f
+/* Return a struct timeval that is roughly equivalent to T.
+   Use the least timeval not less than T.
+   Return an extremal value if the result would overflow.  */
+struct timeval
+make_timeval (EMACS_TIME t)
+{
+  struct timeval tv;
+  tv.tv_sec = t.tv_sec;
+  tv.tv_usec = t.tv_nsec / 1000;
+
+  if (t.tv_nsec % 1000 != 0)
+    {
+      if (tv.tv_usec < 999999)
+       tv.tv_usec++;
+      else if (tv.tv_sec < TYPE_MAXIMUM (time_t))
+       {
+         tv.tv_sec++;
+         tv.tv_usec = 0;
+       }
+    }
+
+  return tv;
+}
+
+/* Set the access and modification time stamps of FD (a.k.a. FILE) to be
+   ATIME and MTIME, respectively.
+   FD must be either negative -- in which case it is ignored --
+   or a file descriptor that is open on FILE.
+   If FD is nonnegative, then FILE can be NULL.  */
 int
-set_file_times (const char *filename, EMACS_TIME atime, EMACS_TIME mtime)
-{
-#ifdef HAVE_UTIMES
-  struct timeval tv[2];
-  tv[0] = atime;
-  tv[1] = mtime;
-  return utimes (filename, tv);
-#else /* not HAVE_UTIMES */
-  struct utimbuf utb;
-  utb.actime = EMACS_SECS (atime);
-  utb.modtime = EMACS_SECS (mtime);
-  return utime (filename, &utb);
-#endif /* not HAVE_UTIMES */
+set_file_times (int fd, const char *filename,
+               EMACS_TIME atime, EMACS_TIME mtime)
+{
+  struct timespec timespec[2];
+  timespec[0] = atime;
+  timespec[1] = mtime;
+  return fdutimens (fd, filename, timespec);
 }
 \f
 /* mkdir and rmdir functions, for systems which don't have them.  */
@@ -2527,6 +2524,50 @@ list_system_processes (void)
   return proclist;
 }
 
+#elif defined BSD_SYSTEM
+
+Lisp_Object
+list_system_processes (void)
+{
+#ifdef DARWIN_OS
+  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
+#else
+  int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PROC};
+#endif
+  size_t len;
+  struct kinfo_proc *procs;
+  size_t i;
+
+  struct gcpro gcpro1;
+  Lisp_Object proclist = Qnil;
+
+  if (sysctl (mib, 3, NULL, &len, NULL, 0) != 0)
+    return proclist;
+
+  procs = xmalloc (len);
+  if (sysctl (mib, 3, procs, &len, NULL, 0) != 0)
+    {
+      xfree (procs);
+      return proclist;
+    }
+
+  GCPRO1 (proclist);
+  len /= sizeof (struct kinfo_proc);
+  for (i = 0; i < len; i++)
+    {
+#ifdef DARWIN_OS
+      proclist = Fcons (make_fixnum_or_float (procs[i].kp_proc.p_pid), proclist);
+#else
+      proclist = Fcons (make_fixnum_or_float (procs[i].ki_pid), proclist);
+#endif
+    }
+  UNGCPRO;
+
+  xfree (procs);
+
+  return  proclist;
+}
+
 /* The WINDOWSNT implementation is in w32.c.
    The MSDOS implementation is in dosfns.c.  */
 #elif !defined (WINDOWSNT) && !defined (MSDOS)
@@ -2540,60 +2581,82 @@ list_system_processes (void)
 #endif /* !defined (WINDOWSNT) */
 
 #ifdef GNU_LINUX
-static void
-time_from_jiffies (unsigned long long tval, long hz,
-                  time_t *sec, unsigned *usec)
-{
-  unsigned long long ullsec;
-
-  *sec = tval / hz;
-  ullsec = *sec;
-  tval -= ullsec * hz;
-  /* Careful: if HZ > 1 million, then integer division by it yields zero.  */
-  if (hz <= 1000000)
-    *usec = tval * 1000000 / hz;
+static EMACS_TIME
+time_from_jiffies (unsigned long long tval, long hz)
+{
+  unsigned long long s = tval / hz;
+  unsigned long long frac = tval % hz;
+  int ns;
+  EMACS_TIME t;
+
+  if (TYPE_MAXIMUM (time_t) < s)
+    time_overflow ();
+  if (LONG_MAX - 1 <= ULLONG_MAX / EMACS_TIME_RESOLUTION
+      || frac <= ULLONG_MAX / EMACS_TIME_RESOLUTION)
+    ns = frac * EMACS_TIME_RESOLUTION / hz;
   else
-    *usec = tval / (hz / 1000000);
+    {
+      /* This is reachable only in the unlikely case that HZ * HZ
+        exceeds ULLONG_MAX.  It calculates an approximation that is
+        guaranteed to be in range.  */
+      long hz_per_ns = (hz / EMACS_TIME_RESOLUTION
+                       + (hz % EMACS_TIME_RESOLUTION != 0));
+      ns = frac / hz_per_ns;
+    }
+
+  EMACS_SET_SECS_NSECS (t, s, ns);
+  return t;
 }
 
 static Lisp_Object
 ltime_from_jiffies (unsigned long long tval, long hz)
 {
-  time_t sec;
-  unsigned usec;
-
-  time_from_jiffies (tval, hz, &sec, &usec);
-
-  return list3 (make_number ((sec >> 16) & 0xffff),
-               make_number (sec & 0xffff),
-               make_number (usec));
+  EMACS_TIME t = time_from_jiffies (tval, hz);
+  return make_lisp_time (t);
 }
 
-static void
-get_up_time (time_t *sec, unsigned *usec)
+static EMACS_TIME
+get_up_time (void)
 {
   FILE *fup;
+  EMACS_TIME up;
 
-  *sec = *usec = 0;
+  EMACS_SET_SECS_NSECS (up, 0, 0);
 
   BLOCK_INPUT;
   fup = fopen ("/proc/uptime", "r");
 
   if (fup)
     {
-      double uptime, idletime;
+      unsigned long long upsec, upfrac, idlesec, idlefrac;
+      int upfrac_start, upfrac_end, idlefrac_start, idlefrac_end;
 
-      /* The numbers in /proc/uptime use C-locale decimal point, but
-        we already set ourselves to the C locale (see `fixup_locale'
-        in emacs.c).  */
-      if (2 <= fscanf (fup, "%lf %lf", &uptime, &idletime))
+      if (fscanf (fup, "%llu.%n%llu%n %llu.%n%llu%n",
+                 &upsec, &upfrac_start, &upfrac, &upfrac_end,
+                 &idlesec, &idlefrac_start, &idlefrac, &idlefrac_end)
+         == 4)
        {
-         *sec = uptime;
-         *usec = (uptime - *sec) * 1000000;
+         if (TYPE_MAXIMUM (time_t) < upsec)
+           {
+             upsec = TYPE_MAXIMUM (time_t);
+             upfrac = EMACS_TIME_RESOLUTION - 1;
+           }
+         else
+           {
+             int upfraclen = upfrac_end - upfrac_start;
+             for (; upfraclen < LOG10_EMACS_TIME_RESOLUTION; upfraclen++)
+               upfrac *= 10;
+             for (; LOG10_EMACS_TIME_RESOLUTION < upfraclen; upfraclen--)
+               upfrac /= 10;
+             upfrac = min (upfrac, EMACS_TIME_RESOLUTION - 1);
+           }
+         EMACS_SET_SECS_NSECS (up, upsec, upfrac);
        }
       fclose (fup);
     }
   UNBLOCK_INPUT;
+
+  return up;
 }
 
 #define MAJOR(d) (((unsigned)(d) >> 8) & 0xfff)
@@ -2693,24 +2756,14 @@ system_process_attributes (Lisp_Object pid)
   unsigned long long u_time, s_time, cutime, cstime, start;
   long priority, niceness, rss;
   unsigned long minflt, majflt, cminflt, cmajflt, vsize;
-  time_t sec;
-  unsigned usec;
-  EMACS_TIME tnow, tstart, tboot, telapsed;
+  EMACS_TIME tnow, tstart, tboot, telapsed, us_time;
   double pcpu, pmem;
   Lisp_Object attrs = Qnil;
   Lisp_Object cmd_str, decoded_cmd, tem;
   struct gcpro gcpro1, gcpro2;
 
   CHECK_NUMBER_OR_FLOAT (pid);
-  if (FLOATP (pid))
-    {
-      double v = XFLOAT_DATA (pid);
-      if (! (TYPE_MINIMUM (pid_t) <= v && v < TYPE_MAXIMUM (pid_t) + 1.0))
-       return attrs;
-      proc_id = v;
-    }
-  else
-    proc_id = XINT (pid);
+  CONS_TO_INTEGER (pid, pid_t, proc_id);
   sprintf (procfn, "/proc/%"pMd, proc_id);
   if (stat (procfn, &st) < 0)
     return attrs;
@@ -2826,35 +2879,18 @@ system_process_attributes (Lisp_Object pid)
          attrs = Fcons (Fcons (Qnice, make_number (niceness)), attrs);
          attrs = Fcons (Fcons (Qthcount, make_fixnum_or_float (thcount_eint)), attrs);
          EMACS_GET_TIME (tnow);
-         get_up_time (&sec, &usec);
-         EMACS_SET_SECS (telapsed, sec);
-         EMACS_SET_USECS (telapsed, usec);
+         telapsed = get_up_time ();
          EMACS_SUB_TIME (tboot, tnow, telapsed);
-         time_from_jiffies (start, clocks_per_sec, &sec, &usec);
-         EMACS_SET_SECS (tstart, sec);
-         EMACS_SET_USECS (tstart, usec);
+         tstart = time_from_jiffies (start, clocks_per_sec);
          EMACS_ADD_TIME (tstart, tboot, tstart);
-         attrs = Fcons (Fcons (Qstart,
-                               list3 (make_number
-                                      ((EMACS_SECS (tstart) >> 16) & 0xffff),
-                                      make_number
-                                      (EMACS_SECS (tstart) & 0xffff),
-                                      make_number
-                                      (EMACS_USECS (tstart)))),
-                        attrs);
+         attrs = Fcons (Fcons (Qstart, make_lisp_time (tstart)), attrs);
          attrs = Fcons (Fcons (Qvsize, make_fixnum_or_float (vsize/1024)), attrs);
          attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (4*rss)), attrs);
          EMACS_SUB_TIME (telapsed, tnow, tstart);
-         attrs = Fcons (Fcons (Qetime,
-                               list3 (make_number
-                                      ((EMACS_SECS (telapsed) >> 16) & 0xffff),
-                                      make_number
-                                      (EMACS_SECS (telapsed) & 0xffff),
-                                      make_number
-                                      (EMACS_USECS (telapsed)))),
-                        attrs);
-         time_from_jiffies (u_time + s_time, clocks_per_sec, &sec, &usec);
-         pcpu = (sec + usec / 1000000.0) / (EMACS_SECS (telapsed) + EMACS_USECS (telapsed) / 1000000.0);
+         attrs = Fcons (Fcons (Qetime, make_lisp_time (telapsed)), attrs);
+         us_time = time_from_jiffies (u_time + s_time, clocks_per_sec);
+         pcpu = (EMACS_TIME_TO_DOUBLE (us_time)
+                 / EMACS_TIME_TO_DOUBLE (telapsed));
          if (pcpu > 1.0)
            pcpu = 1.0;
          attrs = Fcons (Fcons (Qpcpu, make_float (100 * pcpu)), attrs);
@@ -2975,15 +3011,7 @@ system_process_attributes (Lisp_Object pid)
   struct gcpro gcpro1, gcpro2;
 
   CHECK_NUMBER_OR_FLOAT (pid);
-  if (FLOATP (pid))
-    {
-      double v = XFLOAT_DATA (pid);
-      if (! (TYPE_MINIMUM (pid_t) <= v && v < TYPE_MAXIMUM (pid_t) + 1.0))
-       return attrs;
-      proc_id = v;
-    }
-  else
-    proc_id = XINT (v);
+  CONS_TO_INTEGER (pid, pid_t, proc_id);
   sprintf (procfn, "/proc/%"pMd, proc_id);
   if (stat (procfn, &st) < 0)
     return attrs;
@@ -3043,33 +3071,20 @@ system_process_attributes (Lisp_Object pid)
                Qcstime
                Are they available? */
 
-         attrs = Fcons (Fcons (Qtime,
-                               list3 (make_number (pinfo.pr_time.tv_sec >> 16),
-                                      make_number (pinfo.pr_time.tv_sec & 0xffff),
-                                      make_number (pinfo.pr_time.tv_nsec))),
-                        attrs);
-
-         attrs = Fcons (Fcons (Qctime,
-                               list3 (make_number (pinfo.pr_ctime.tv_sec >> 16),
-                                      make_number (pinfo.pr_ctime.tv_sec & 0xffff),
-                                      make_number (pinfo.pr_ctime.tv_nsec))),
-                        attrs);
-
+         attrs = Fcons (Fcons (Qtime, make_lisp_time (pinfo.pr_time)), attrs);
+         attrs = Fcons (Fcons (Qctime, make_lisp_time (pinfo.pr_ctime)), attrs);
          attrs = Fcons (Fcons (Qpri, make_number (pinfo.pr_lwp.pr_pri)), attrs);
          attrs = Fcons (Fcons (Qnice, make_number (pinfo.pr_lwp.pr_nice)), attrs);
          attrs = Fcons (Fcons (Qthcount, make_fixnum_or_float (pinfo.pr_nlwp)), attrs);
 
-         attrs = Fcons (Fcons (Qstart,
-                               list3 (make_number (pinfo.pr_start.tv_sec >> 16),
-                                      make_number (pinfo.pr_start.tv_sec & 0xffff),
-                                      make_number (pinfo.pr_start.tv_nsec))),
-                        attrs);
+         attrs = Fcons (Fcons (Qstart, make_lisp_time (pinfo.pr_start)), attrs);
          attrs = Fcons (Fcons (Qvsize, make_fixnum_or_float (pinfo.pr_size)), attrs);
          attrs = Fcons (Fcons (Qrss, make_fixnum_or_float (pinfo.pr_rssize)), attrs);
 
-         /* pr_pctcpu and pr_pctmem are encoded as a fixed point 16 bit number in  [0 ... 1].  */
-         attrs = Fcons (Fcons (Qpcpu, (pinfo.pr_pctcpu * 100.0) / (double)0x8000), attrs);
-         attrs = Fcons (Fcons (Qpmem, (pinfo.pr_pctmem * 100.0) / (double)0x8000), attrs);
+         /* pr_pctcpu and pr_pctmem are unsigned integers in the
+            range 0 .. 2**15, representing 0.0 .. 1.0.  */
+         attrs = Fcons (Fcons (Qpcpu, make_float (100.0 / 0x8000 * pinfo.pr_pctcpu)), attrs);
+         attrs = Fcons (Fcons (Qpmem, make_float (100.0 / 0x8000 * pinfo.pr_pctmem)), attrs);
 
          decoded_cmd
            =  code_convert_string_norecord (make_unibyte_string (pinfo.pr_fname,
@@ -3090,6 +3105,196 @@ system_process_attributes (Lisp_Object pid)
   return attrs;
 }
 
+#elif defined __FreeBSD__
+
+static EMACS_TIME
+timeval_to_EMACS_TIME (struct timeval t)
+{
+  EMACS_TIME e;
+  EMACS_SET_SECS_NSECS (e, t.tv_sec, t.tv_usec * 1000);
+  return e;
+}
+
+static Lisp_Object
+make_lisp_timeval (struct timeval t)
+{
+  return make_lisp_time (timeval_to_EMACS_TIME (t));
+}
+
+Lisp_Object
+system_process_attributes (Lisp_Object pid)
+{
+  int proc_id;
+  int pagesize = getpagesize ();
+  int npages;
+  int fscale;
+  struct passwd *pw;
+  struct group  *gr;
+  char *ttyname;
+  size_t len;
+  char args[MAXPATHLEN];
+  EMACS_TIME t, now;
+
+  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID};
+  struct kinfo_proc proc;
+  size_t proclen = sizeof proc;
+
+  struct gcpro gcpro1, gcpro2;
+  Lisp_Object attrs = Qnil;
+  Lisp_Object decoded_comm;
+
+  CHECK_NUMBER_OR_FLOAT (pid);
+  CONS_TO_INTEGER (pid, int, proc_id);
+  mib[3] = proc_id;
+
+  if (sysctl (mib, 4, &proc, &proclen, NULL, 0) != 0)
+    return attrs;
+
+  GCPRO2 (attrs, decoded_comm);
+
+  attrs = Fcons (Fcons (Qeuid, make_fixnum_or_float (proc.ki_uid)), attrs);
+
+  BLOCK_INPUT;
+  pw = getpwuid (proc.ki_uid);
+  UNBLOCK_INPUT;
+  if (pw)
+    attrs = Fcons (Fcons (Quser, build_string (pw->pw_name)), attrs);
+
+  attrs = Fcons (Fcons (Qegid, make_fixnum_or_float (proc.ki_svgid)), attrs);
+
+  BLOCK_INPUT;
+  gr = getgrgid (proc.ki_svgid);
+  UNBLOCK_INPUT;
+  if (gr)
+    attrs = Fcons (Fcons (Qgroup, build_string (gr->gr_name)), attrs);
+
+  decoded_comm = code_convert_string_norecord
+    (make_unibyte_string (proc.ki_comm, strlen (proc.ki_comm)),
+     Vlocale_coding_system, 0);
+
+  attrs = Fcons (Fcons (Qcomm, decoded_comm), attrs);
+  {
+    char state[2] = {'\0', '\0'};
+    switch (proc.ki_stat)
+      {
+      case SRUN:
+       state[0] = 'R';
+       break;
+
+      case SSLEEP:
+       state[0] = 'S';
+       break;
+
+      case SLOCK:
+       state[0] = 'D';
+       break;
+
+      case SZOMB:
+       state[0] = 'Z';
+       break;
+
+      case SSTOP:
+       state[0] = 'T';
+       break;
+      }
+    attrs = Fcons (Fcons (Qstate, build_string (state)), attrs);
+  }
+
+  attrs = Fcons (Fcons (Qppid, make_fixnum_or_float (proc.ki_ppid)), attrs);
+  attrs = Fcons (Fcons (Qpgrp, make_fixnum_or_float (proc.ki_pgid)), attrs);
+  attrs = Fcons (Fcons (Qsess, make_fixnum_or_float (proc.ki_sid)),  attrs);
+
+  BLOCK_INPUT;
+  ttyname = proc.ki_tdev == NODEV ? NULL : devname (proc.ki_tdev, S_IFCHR);
+  UNBLOCK_INPUT;
+  if (ttyname)
+    attrs = Fcons (Fcons (Qtty, build_string (ttyname)), attrs);
+
+  attrs = Fcons (Fcons (Qtpgid,   make_fixnum_or_float (proc.ki_tpgid)), attrs);
+  attrs = Fcons (Fcons (Qminflt,  make_fixnum_or_float (proc.ki_rusage.ru_minflt)), attrs);
+  attrs = Fcons (Fcons (Qmajflt,  make_fixnum_or_float (proc.ki_rusage.ru_majflt)), attrs);
+  attrs = Fcons (Fcons (Qcminflt, make_number (proc.ki_rusage_ch.ru_minflt)), attrs);
+  attrs = Fcons (Fcons (Qcmajflt, make_number (proc.ki_rusage_ch.ru_majflt)), attrs);
+
+  attrs = Fcons (Fcons (Qutime, make_lisp_timeval (proc.ki_rusage.ru_utime)),
+                attrs);
+  attrs = Fcons (Fcons (Qstime, make_lisp_timeval (proc.ki_rusage.ru_stime)),
+                attrs);
+  EMACS_ADD_TIME (t,
+                 timeval_to_EMACS_TIME (proc.ki_rusage.ru_utime),
+                 timeval_to_EMACS_TIME (proc.ki_rusage.ru_stime));
+  attrs = Fcons (Fcons (Qtime, make_lisp_time (t)), attrs);
+
+  attrs = Fcons (Fcons (Qcutime,
+                       make_lisp_timeval (proc.ki_rusage_ch.ru_utime)),
+                attrs);
+  attrs = Fcons (Fcons (Qcstime,
+                       make_lisp_timeval (proc.ki_rusage_ch.ru_utime)),
+                attrs);
+  EMACS_ADD_TIME (t,
+                 timeval_to_EMACS_TIME (proc.ki_rusage_ch.ru_utime),
+                 timeval_to_EMACS_TIME (proc.ki_rusage_ch.ru_stime));
+  attrs = Fcons (Fcons (Qctime, make_lisp_time (t)), attrs);
+
+  attrs = Fcons (Fcons (Qthcount, make_fixnum_or_float (proc.ki_numthreads)),
+                attrs);
+  attrs = Fcons (Fcons (Qpri,   make_number (proc.ki_pri.pri_native)), attrs);
+  attrs = Fcons (Fcons (Qnice,  make_number (proc.ki_nice)), attrs);
+  attrs = Fcons (Fcons (Qstart, make_lisp_timeval (proc.ki_start)), attrs);
+  attrs = Fcons (Fcons (Qvsize, make_number (proc.ki_size >> 10)), attrs);
+  attrs = Fcons (Fcons (Qrss,   make_number (proc.ki_rssize * pagesize >> 10)),
+                attrs);
+
+  EMACS_GET_TIME (now);
+  EMACS_SUB_TIME (t, now, timeval_to_EMACS_TIME (proc.ki_start));
+  attrs = Fcons (Fcons (Qetime, make_lisp_time (t)), attrs);
+
+  len = sizeof fscale;
+  if (sysctlbyname ("kern.fscale", &fscale, &len, NULL, 0) == 0)
+    {
+      double pcpu;
+      fixpt_t ccpu;
+      len = sizeof ccpu;
+      if (sysctlbyname ("kern.ccpu", &ccpu, &len, NULL, 0) == 0)
+       {
+         pcpu = (100.0 * proc.ki_pctcpu / fscale
+                 / (1 - exp (proc.ki_swtime * log ((double) ccpu / fscale))));
+         attrs = Fcons (Fcons (Qpcpu, make_fixnum_or_float (pcpu)), attrs);
+       }
+    }
+
+  len = sizeof npages;
+  if (sysctlbyname ("hw.availpages", &npages, &len, NULL, 0) == 0)
+    {
+      double pmem = (proc.ki_flag & P_INMEM
+                    ? 100.0 * proc.ki_rssize / npages
+                    : 0);
+      attrs = Fcons (Fcons (Qpmem, make_fixnum_or_float (pmem)), attrs);
+    }
+
+  mib[2] = KERN_PROC_ARGS;
+  len = MAXPATHLEN;
+  if (sysctl (mib, 4, args, &len, NULL, 0) == 0)
+    {
+      int i;
+      for (i = 0; i < len; i++)
+       {
+         if (! args[i] && i < len - 1)
+           args[i] = ' ';
+       }
+
+      decoded_comm =
+       (code_convert_string_norecord
+        (make_unibyte_string (args, strlen (args)),
+         Vlocale_coding_system, 0));
+
+      attrs = Fcons (Fcons (Qargs, decoded_comm), attrs);
+    }
+
+  UNGCPRO;
+  return attrs;
+}
+
 /* The WINDOWSNT implementation is in w32.c.
    The MSDOS implementation is in dosfns.c.  */
 #elif !defined (WINDOWSNT) && !defined (MSDOS)