]> code.delx.au - gnu-emacs/blobdiff - src/sysdep.c
Fix typo in previous change's ChangeLog.
[gnu-emacs] / src / sysdep.c
index 78e3d908cfea4d418fa15ad79ded7ad4b5aa660b..34fd8b1044b19377288c0e1b9ed9fabc3ebe6c58 100644 (file)
@@ -1,5 +1,5 @@
 /* Interfaces to system-dependent kernel and library entries.
-   Copyright (C) 1985-1988, 1993-1995, 1999-2013 Free Software
+   Copyright (C) 1985-1988, 1993-1995, 1999-2014 Free Software
    Foundation, Inc.
 
 This file is part of GNU Emacs.
@@ -19,7 +19,13 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
-#define SYSTIME_INLINE EXTERN_INLINE
+/* If HYBRID_GET_CURRENT_DIR_NAME is defined in conf_post.h, then we
+   need the following before including unistd.h, in order to pick up
+   the right prototype for gget_current_dir_name.  */
+#ifdef HYBRID_GET_CURRENT_DIR_NAME
+#undef get_current_dir_name
+#define get_current_dir_name gget_current_dir_name
+#endif
 
 #include <execinfo.h>
 #include "sysstdio.h"
@@ -48,7 +54,6 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 # include <sys/user.h>
 # undef frame
 
-# include <sys/resource.h>
 # include <math.h>
 #endif
 
@@ -74,6 +79,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include "msdos.h"
 #endif
 
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
 #include <sys/param.h>
 #include <sys/file.h>
 #include <fcntl.h>
@@ -107,9 +115,6 @@ int _cdecl _getpid (void);
 #include "syssignal.h"
 #include "systime.h"
 
-static void emacs_get_tty (int, struct emacs_tty *);
-static int emacs_set_tty (int, struct emacs_tty *, bool);
-
 /* ULLONG_MAX is missing on Red Hat Linux 7.3; see Bug#11781.  */
 #ifndef ULLONG_MAX
 #define ULLONG_MAX TYPE_MAXIMUM (unsigned long long int)
@@ -124,13 +129,12 @@ static const int baud_convert[] =
     1800, 2400, 4800, 9600, 19200, 38400
   };
 
-
-#if !defined (HAVE_GET_CURRENT_DIR_NAME) || defined (BROKEN_GET_CURRENT_DIR_NAME)
-
+#if !defined HAVE_GET_CURRENT_DIR_NAME || defined BROKEN_GET_CURRENT_DIR_NAME \
+  || (defined HYBRID_GET_CURRENT_DIR_NAME)
 /* Return the current working directory.  Returns NULL on errors.
    Any other returned value must be freed with free. This is used
    only when get_current_dir_name is not defined on the system.  */
-char*
+char *
 get_current_dir_name (void)
 {
   char *buf;
@@ -224,7 +228,9 @@ discard_tty_input (void)
 void
 stuff_char (char c)
 {
-  if (! FRAME_TERMCAP_P (SELECTED_FRAME ()))
+  if (! (FRAMEP (selected_frame)
+        && FRAME_LIVE_P (XFRAME (selected_frame))
+        && FRAME_TERMCAP_P (XFRAME (selected_frame))))
     return;
 
 /* Should perhaps error if in batch mode */
@@ -257,7 +263,7 @@ init_baud_rate (int fd)
 #endif /* not DOS_NT */
     }
 
-  baud_rate = (emacs_ospeed < sizeof baud_convert / sizeof baud_convert[0]
+  baud_rate = (emacs_ospeed < ARRAYELTS (baud_convert)
               ? baud_convert[emacs_ospeed] : 9600);
   if (baud_rate == 0)
     baud_rate = 1200;
@@ -306,7 +312,7 @@ get_child_status (pid_t child, int *status, int options, bool interruptible)
   /* If successful and status is requested, tell wait_reading_process_output
      that it needs to wake up and look around.  */
   if (pid && status && input_available_clear_time)
-    *input_available_clear_time = make_emacs_time (0, 0);
+    *input_available_clear_time = make_timespec (0, 0);
 
   return pid;
 }
@@ -466,15 +472,29 @@ sys_subshell (void)
 {
 #ifdef DOS_NT  /* Demacs 1.1.2 91/10/20 Manabu Higashida */
   int st;
+#ifdef MSDOS
   char oldwd[MAXPATHLEN+1]; /* Fixed length is safe on MSDOS.  */
+#else
+  char oldwd[MAX_UTF8_PATH];
+#endif
 #endif
   pid_t pid;
   int status;
   struct save_signal saved_handlers[5];
-  Lisp_Object dir;
-  unsigned char *volatile str_volatile = 0;
-  unsigned char *str;
-  int len;
+  char *str = SSDATA (encode_current_directory ());
+
+#ifdef DOS_NT
+  pid = 0;
+#else
+  {
+    char *volatile str_volatile = str;
+    pid = vfork ();
+    str = str_volatile;
+  }
+#endif
+
+  if (pid < 0)
+    error ("Can't spawn subshell");
 
   saved_handlers[0].code = SIGINT;
   saved_handlers[1].code = SIGQUIT;
@@ -486,31 +506,8 @@ sys_subshell (void)
   saved_handlers[3].code = 0;
 #endif
 
-  /* Mentioning current_buffer->buffer would mean including buffer.h,
-     which somehow wedges the hp compiler.  So instead...  */
-
-  dir = intern ("default-directory");
-  if (NILP (Fboundp (dir)))
-    goto xyzzy;
-  dir = Fsymbol_value (dir);
-  if (!STRINGP (dir))
-    goto xyzzy;
-
-  dir = expand_and_dir_to_file (Funhandled_file_name_directory (dir), Qnil);
-  str_volatile = str = alloca (SCHARS (dir) + 2);
-  len = SCHARS (dir);
-  memcpy (str, SDATA (dir), len);
-  if (str[len - 1] != '/') str[len++] = '/';
-  str[len] = 0;
- xyzzy:
-
 #ifdef DOS_NT
-  pid = 0;
   save_signal_handlers (saved_handlers);
-#else
-  pid = vfork ();
-  if (pid == -1)
-    error ("Can't spawn subshell");
 #endif
 
   if (pid == 0)
@@ -528,11 +525,10 @@ sys_subshell (void)
        sh = "sh";
 
       /* Use our buffer's default directory for the subshell.  */
-      str = str_volatile;
-      if (str && chdir ((char *) str) != 0)
+      if (chdir (str) != 0)
        {
 #ifndef DOS_NT
-         emacs_perror ((char *) str);
+         emacs_perror (str);
          _exit (EXIT_CANCELED);
 #endif
        }
@@ -546,8 +542,6 @@ sys_subshell (void)
        if (epwd)
          {
            strcpy (old_pwd, epwd);
-           if (str[len - 1] == '/')
-             str[len - 1] = '\0';
            setenv ("PWD", str, 1);
          }
        st = system (sh);
@@ -604,7 +598,7 @@ restore_signal_handlers (struct save_signal *saved_handlers)
 }
 \f
 #ifdef USABLE_SIGIO
-static int old_fcntl_flags[MAXDESC];
+static int old_fcntl_flags[FD_SETSIZE];
 #endif
 
 void
@@ -617,6 +611,7 @@ init_sigio (int fd)
 #endif
 }
 
+#ifndef DOS_NT
 static void
 reset_sigio (int fd)
 {
@@ -624,6 +619,7 @@ reset_sigio (int fd)
   fcntl (fd, F_SETFL, old_fcntl_flags[fd]);
 #endif
 }
+#endif
 
 void
 request_sigio (void)
@@ -671,7 +667,29 @@ ignore_sigio (void)
   signal (SIGIO, SIG_IGN);
 #endif
 }
+\f
+#ifndef MSDOS
+/* Block SIGCHLD.  */
+
+void
+block_child_signal (sigset_t *oldset)
+{
+  sigset_t blocked;
+  sigemptyset (&blocked);
+  sigaddset (&blocked, SIGCHLD);
+  sigaddset (&blocked, SIGINT);
+  pthread_sigmask (SIG_BLOCK, &blocked, oldset);
+}
 
+/* Unblock SIGCHLD.  */
+
+void
+unblock_child_signal (sigset_t const *oldset)
+{
+  pthread_sigmask (SIG_SETMASK, oldset, 0);
+}
+
+#endif /* !MSDOS */
 \f
 /* Saving and restoring the process group of Emacs's terminal.  */
 
@@ -706,21 +724,21 @@ init_foreground_group (void)
 /* Block and unblock SIGTTOU.  */
 
 void
-block_tty_out_signal (void)
+block_tty_out_signal (sigset_t *oldset)
 {
 #ifdef SIGTTOU
   sigset_t blocked;
   sigemptyset (&blocked);
   sigaddset (&blocked, SIGTTOU);
-  pthread_sigmask (SIG_BLOCK, &blocked, 0);
+  pthread_sigmask (SIG_BLOCK, &blocked, oldset);
 #endif
 }
 
 void
-unblock_tty_out_signal (void)
+unblock_tty_out_signal (sigset_t const *oldset)
 {
 #ifdef SIGTTOU
-  pthread_sigmask (SIG_SETMASK, &empty_mask, 0);
+  pthread_sigmask (SIG_SETMASK, oldset, 0);
 #endif
 }
 
@@ -735,10 +753,11 @@ static void
 tcsetpgrp_without_stopping (int fd, pid_t pgid)
 {
 #ifdef SIGTTOU
+  sigset_t oldset;
   block_input ();
-  block_tty_out_signal ();
+  block_tty_out_signal (&oldset);
   tcsetpgrp (fd, pgid);
-  unblock_tty_out_signal ();
+  unblock_tty_out_signal (&oldset);
   unblock_input ();
 #endif
 }
@@ -765,15 +784,28 @@ widen_foreground_group (int fd)
 /* Getting and setting emacs_tty structures.  */
 
 /* Set *TC to the parameters associated with the terminal FD,
-   or clear it if the parameters are not available.  */
-static void
+   or clear it if the parameters are not available.
+   Return 0 on success, -1 on failure.  */
+int
 emacs_get_tty (int fd, struct emacs_tty *settings)
 {
   /* Retrieve the primary parameters - baud rate, character size, etcetera.  */
-#ifndef DOS_NT
-  /* We have those nifty POSIX tcmumbleattr functions.  */
   memset (&settings->main, 0, sizeof (settings->main));
-  tcgetattr (fd, &settings->main);
+#ifdef DOS_NT
+#ifdef WINDOWSNT
+  HANDLE h = (HANDLE)_get_osfhandle (fd);
+  DWORD console_mode;
+
+  if (h && h != INVALID_HANDLE_VALUE && GetConsoleMode (h, &console_mode))
+    {
+      settings->main = console_mode;
+      return 0;
+    }
+#endif /* WINDOWSNT */
+  return -1;
+#else  /* !DOS_NT */
+  /* We have those nifty POSIX tcmumbleattr functions.  */
+  return tcgetattr (fd, &settings->main);
 #endif
 }
 
@@ -782,11 +814,26 @@ emacs_get_tty (int fd, struct emacs_tty *settings)
    *SETTINGS.  If FLUSHP, discard input.
    Return 0 if all went well, and -1 (setting errno) if anything failed.  */
 
-static int
+int
 emacs_set_tty (int fd, struct emacs_tty *settings, bool flushp)
 {
   /* Set the primary parameters - baud rate, character size, etcetera.  */
-#ifndef DOS_NT
+#ifdef DOS_NT
+#ifdef WINDOWSNT
+  HANDLE h = (HANDLE)_get_osfhandle (fd);
+
+  if (h && h != INVALID_HANDLE_VALUE)
+    {
+      DWORD new_mode;
+
+      /* Assume the handle is open for input.  */
+      if (flushp)
+       FlushConsoleInputBuffer (h);
+      new_mode = settings->main;
+      SetConsoleMode (h, new_mode);
+    }
+#endif /* WINDOWSNT */
+#else  /* !DOS_NT */
   int i;
   /* We have those nifty POSIX tcmumbleattr functions.
      William J. Smith <wjs@wiis.wang.com> writes:
@@ -833,7 +880,7 @@ emacs_set_tty (int fd, struct emacs_tty *settings, bool flushp)
 \f
 
 #ifdef F_SETOWN
-static int old_fcntl_owner[MAXDESC];
+static int old_fcntl_owner[FD_SETSIZE];
 #endif /* F_SETOWN */
 
 /* This may also be defined in stdio,
@@ -1127,6 +1174,24 @@ tabs_safe_p (int fd)
   return 0;
 #endif /* DOS_NT */
 }
+
+/* Discard echoing.  */
+
+void
+suppress_echo_on_tty (int fd)
+{
+  struct emacs_tty etty;
+
+  emacs_get_tty (fd, &etty);
+#ifdef DOS_NT
+  /* Set raw input mode.  */
+  etty.main = 0;
+#else
+  etty.main.c_lflag &= ~ICANON;        /* Disable buffering */
+  etty.main.c_lflag &= ~ECHO;  /* Disable echoing */
+#endif /* ! WINDOWSNT */
+  emacs_set_tty (fd, &etty, 0);
+}
 \f
 /* Get terminal size from system.
    Store number of lines into *HEIGHTP and width into *WIDTHP.
@@ -1186,7 +1251,8 @@ get_tty_size (int fd, int *widthp, int *heightp)
 }
 
 /* Set the logical window size associated with descriptor FD
-   to HEIGHT and WIDTH.  This is used mainly with ptys.  */
+   to HEIGHT and WIDTH.  This is used mainly with ptys.
+   Return a negative value on failure.  */
 
 int
 set_window_size (int fd, int height, int width)
@@ -1198,10 +1264,7 @@ set_window_size (int fd, int height, int width)
   size.ws_row = height;
   size.ws_col = width;
 
-  if (ioctl (fd, TIOCSWINSZ, &size) == -1)
-    return 0; /* error */
-  else
-    return 1;
+  return ioctl (fd, TIOCSWINSZ, &size);
 
 #else
 #ifdef TIOCSSIZE
@@ -1211,10 +1274,7 @@ set_window_size (int fd, int height, int width)
   size.ts_lines = height;
   size.ts_cols = width;
 
-  if (ioctl (fd, TIOCGSIZE, &size) == -1)
-    return 0;
-  else
-    return 1;
+  return ioctl (fd, TIOCGSIZE, &size);
 #else
   return -1;
 #endif /* not SunOS-style */
@@ -1264,7 +1324,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
       int i;
       tty_turn_off_insert (tty_out);
 
-      for (i = curX (tty_out); i < FrameCols (tty_out) - 1; i++)
+      for (i = cursorX (tty_out); i < FrameCols (tty_out) - 1; i++)
         {
           fputc (' ', tty_out->output);
         }
@@ -1525,7 +1585,9 @@ emacs_sigaction_init (struct sigaction *action, signal_handler_t handler)
   /* When handling a signal, block nonfatal system signals that are caught
      by Emacs.  This makes race conditions less likely.  */
   sigaddset (&action->sa_mask, SIGALRM);
+#ifdef SIGCHLD
   sigaddset (&action->sa_mask, SIGCHLD);
+#endif
 #ifdef SIGDANGER
   sigaddset (&action->sa_mask, SIGDANGER);
 #endif
@@ -1544,9 +1606,6 @@ emacs_sigaction_init (struct sigaction *action, signal_handler_t handler)
 #endif
     }
 
-  if (! IEEE_FLOATING_POINT)
-    sigaddset (&action->sa_mask, SIGFPE);
-
   action->sa_handler = handler;
   action->sa_flags = emacs_sigaction_flags ();
 }
@@ -1666,6 +1725,83 @@ handle_arith_signal (int sig)
   xsignal0 (Qarith_error);
 }
 
+#ifdef HAVE_STACK_OVERFLOW_HANDLING
+
+/* -1 if stack grows down as expected on most OS/ABI variants, 1 otherwise.  */
+
+static int stack_direction;
+
+/* Alternate stack used by SIGSEGV handler below.  */
+
+static unsigned char sigsegv_stack[SIGSTKSZ];
+
+/* Attempt to recover from SIGSEGV caused by C stack overflow.  */
+
+static void
+handle_sigsegv (int sig, siginfo_t *siginfo, void *arg)
+{
+  /* Hard GC error may lead to stack overflow caused by
+     too nested calls to mark_object.  No way to survive.  */
+  if (!gc_in_progress)
+    {
+      struct rlimit rlim;
+
+      if (!getrlimit (RLIMIT_STACK, &rlim))
+       {
+         enum { STACK_DANGER_ZONE = 16 * 1024 };
+         char *beg, *end, *addr;
+
+         beg = stack_bottom;
+         end = stack_bottom + stack_direction * rlim.rlim_cur;
+         if (beg > end)
+           addr = beg, beg = end, end = addr;
+         addr = (char *) siginfo->si_addr;
+         /* If we're somewhere on stack and too close to
+            one of its boundaries, most likely this is it.  */
+         if (beg < addr && addr < end
+             && (addr - beg < STACK_DANGER_ZONE
+                 || end - addr < STACK_DANGER_ZONE))
+           siglongjmp (return_to_command_loop, 1);
+       }
+    }
+
+  /* Otherwise we can't do anything with this.  */
+  deliver_fatal_thread_signal (sig);
+}
+
+/* Return true if we have successfully set up SIGSEGV handler on alternate
+   stack.  Otherwise we just treat SIGSEGV among the rest of fatal signals.  */
+
+static bool
+init_sigsegv (void)
+{
+  struct sigaction sa;
+  stack_t ss;
+
+  stack_direction = ((char *) &ss < stack_bottom) ? -1 : 1;
+
+  ss.ss_sp = sigsegv_stack;
+  ss.ss_size = sizeof (sigsegv_stack);
+  ss.ss_flags = 0;
+  if (sigaltstack (&ss, NULL) < 0)
+    return 0;
+
+  sigfillset (&sa.sa_mask);
+  sa.sa_sigaction = handle_sigsegv;
+  sa.sa_flags = SA_SIGINFO | SA_ONSTACK | emacs_sigaction_flags ();
+  return sigaction (SIGSEGV, &sa, NULL) < 0 ? 0 : 1;
+}
+
+#else /* not HAVE_STACK_OVERFLOW_HANDLING */
+
+static bool
+init_sigsegv (void)
+{
+  return 0;
+}
+
+#endif /* HAVE_STACK_OVERFLOW_HANDLING */
+
 static void
 deliver_arith_signal (int sig)
 {
@@ -1730,7 +1866,9 @@ init_signals (bool dumping)
 # ifdef SIGBUS
       sys_siglist[SIGBUS] = "Bus error";
 # endif
+# ifdef SIGCHLD
       sys_siglist[SIGCHLD] = "Child status changed";
+# endif
 # ifdef SIGCONT
       sys_siglist[SIGCONT] = "Continued";
 # endif
@@ -1930,7 +2068,8 @@ init_signals (bool dumping)
 #ifdef SIGBUS
   sigaction (SIGBUS, &thread_fatal_action, 0);
 #endif
-  sigaction (SIGSEGV, &thread_fatal_action, 0);
+  if (!init_sigsegv ())
+    sigaction (SIGSEGV, &thread_fatal_action, 0);
 #ifdef SIGSYS
   sigaction (SIGSYS, &thread_fatal_action, 0);
 #endif
@@ -2042,8 +2181,8 @@ seed_random (void *seed, ptrdiff_t seed_size)
 void
 init_random (void)
 {
-  EMACS_TIME t = current_emacs_time ();
-  uintmax_t v = getpid () ^ EMACS_SECS (t) ^ EMACS_NSECS (t);
+  struct timespec t = current_timespec ();
+  uintmax_t v = getpid () ^ t.tv_sec ^ t.tv_nsec;
   seed_random (&v, sizeof v);
 }
 
@@ -2148,6 +2287,7 @@ emacs_abort (void)
 #endif
 
 /* Open FILE for Emacs use, using open flags OFLAG and mode MODE.
+   Use binary I/O on systems that care about text vs binary I/O.
    Arrange for subprograms to not inherit the file descriptor.
    Prefer a method that is multithread-safe, if available.
    Do not fail merely because the open was interrupted by a signal.
@@ -2157,6 +2297,8 @@ int
 emacs_open (const char *file, int oflags, int mode)
 {
   int fd;
+  if (! (oflags & O_TEXT))
+    oflags |= O_BINARY;
   oflags |= O_CLOEXEC;
   while ((fd = open (file, oflags, mode)) < 0 && errno == EINTR)
     QUIT;
@@ -2201,13 +2343,17 @@ emacs_fopen (char const *file, char const *mode)
 int
 emacs_pipe (int fd[2])
 {
-  int result = pipe2 (fd, O_CLOEXEC);
+#ifdef MSDOS
+  return pipe (fd);
+#else  /* !MSDOS */
+  int result = pipe2 (fd, O_BINARY | O_CLOEXEC);
   if (! O_CLOEXEC && result == 0)
     {
       fcntl (fd[0], F_SETFD, FD_CLOEXEC);
       fcntl (fd[1], F_SETFD, FD_CLOEXEC);
     }
   return result;
+#endif /* !MSDOS */
 }
 
 /* Approximate posix_close and POSIX_CLOSE_RESTART well enough for Emacs.
@@ -2280,9 +2426,9 @@ emacs_close (int fd)
    Return the number of bytes read, which might be less than NBYTE.
    On error, set errno and return -1.  */
 ptrdiff_t
-emacs_read (int fildes, char *buf, ptrdiff_t nbyte)
+emacs_read (int fildes, void *buf, ptrdiff_t nbyte)
 {
-  register ssize_t rtnval;
+  ssize_t rtnval;
 
   /* There is no need to check against MAX_RW_COUNT, since no caller ever
      passes a size that large to emacs_read.  */
@@ -2333,14 +2479,14 @@ emacs_full_write (int fildes, char const *buf, ptrdiff_t nbyte,
    interrupted or if a partial write occurs.  Return the number of
    bytes written, setting errno if this is less than NBYTE.  */
 ptrdiff_t
-emacs_write (int fildes, char const *buf, ptrdiff_t nbyte)
+emacs_write (int fildes, void const *buf, ptrdiff_t nbyte)
 {
   return emacs_full_write (fildes, buf, nbyte, 0);
 }
 
 /* Like emacs_write, but also process pending signals if interrupted.  */
 ptrdiff_t
-emacs_write_sig (int fildes, char const *buf, ptrdiff_t nbyte)
+emacs_write_sig (int fildes, void const *buf, ptrdiff_t nbyte)
 {
   return emacs_full_write (fildes, buf, nbyte, 1);
 }
@@ -2378,7 +2524,7 @@ emacs_perror (char const *message)
    Use the least timeval not less than T.
    Return an extremal value if the result would overflow.  */
 struct timeval
-make_timeval (EMACS_TIME t)
+make_timeval (struct timespec t)
 {
   struct timeval tv;
   tv.tv_sec = t.tv_sec;
@@ -2405,7 +2551,7 @@ make_timeval (EMACS_TIME t)
    If FD is nonnegative, then FILE can be NULL.  */
 int
 set_file_times (int fd, const char *filename,
-               EMACS_TIME atime, EMACS_TIME mtime)
+               struct timespec atime, struct timespec mtime)
 {
   struct timespec timespec[2];
   timespec[0] = atime;
@@ -2475,7 +2621,7 @@ serial_configure (struct Lisp_Process *p,
   Lisp_Object childp2 = Qnil;
   Lisp_Object tem = Qnil;
   struct termios attr;
-  int err = -1;
+  int err;
   char summary[4] = "???"; /* This usually becomes "8N1".  */
 
   childp2 = Fcopy_sequence (p->childp);
@@ -2722,7 +2868,7 @@ list_system_processes (void)
 #endif /* !defined (WINDOWSNT) */
 
 #if defined GNU_LINUX && defined HAVE_LONG_LONG_INT
-static EMACS_TIME
+static struct timespec
 time_from_jiffies (unsigned long long tval, long hz)
 {
   unsigned long long s = tval / hz;
@@ -2731,34 +2877,34 @@ time_from_jiffies (unsigned long long tval, long hz)
 
   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;
+  if (LONG_MAX - 1 <= ULLONG_MAX / TIMESPEC_RESOLUTION
+      || frac <= ULLONG_MAX / TIMESPEC_RESOLUTION)
+    ns = frac * TIMESPEC_RESOLUTION / hz;
   else
     {
       /* 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));
+      long hz_per_ns = (hz / TIMESPEC_RESOLUTION
+                       + (hz % TIMESPEC_RESOLUTION != 0));
       ns = frac / hz_per_ns;
     }
 
-  return make_emacs_time (s, ns);
+  return make_timespec (s, ns);
 }
 
 static Lisp_Object
 ltime_from_jiffies (unsigned long long tval, long hz)
 {
-  EMACS_TIME t = time_from_jiffies (tval, hz);
+  struct timespec t = time_from_jiffies (tval, hz);
   return make_lisp_time (t);
 }
 
-static EMACS_TIME
+static struct timespec
 get_up_time (void)
 {
   FILE *fup;
-  EMACS_TIME up = make_emacs_time (0, 0);
+  struct timespec up = make_timespec (0, 0);
 
   block_input ();
   fup = emacs_fopen ("/proc/uptime", "r");
@@ -2776,18 +2922,18 @@ get_up_time (void)
          if (TYPE_MAXIMUM (time_t) < upsec)
            {
              upsec = TYPE_MAXIMUM (time_t);
-             upfrac = EMACS_TIME_RESOLUTION - 1;
+             upfrac = TIMESPEC_RESOLUTION - 1;
            }
          else
            {
              int upfraclen = upfrac_end - upfrac_start;
-             for (; upfraclen < LOG10_EMACS_TIME_RESOLUTION; upfraclen++)
+             for (; upfraclen < LOG10_TIMESPEC_RESOLUTION; upfraclen++)
                upfrac *= 10;
-             for (; LOG10_EMACS_TIME_RESOLUTION < upfraclen; upfraclen--)
+             for (; LOG10_TIMESPEC_RESOLUTION < upfraclen; upfraclen--)
                upfrac /= 10;
-             upfrac = min (upfrac, EMACS_TIME_RESOLUTION - 1);
+             upfrac = min (upfrac, TIMESPEC_RESOLUTION - 1);
            }
-         up = make_emacs_time (upsec, upfrac);
+         up = make_timespec (upsec, upfrac);
        }
       fclose (fup);
     }
@@ -2842,29 +2988,41 @@ procfs_ttyname (int rdev)
   return build_string (name);
 }
 
-static unsigned long
+static uintmax_t
 procfs_get_total_memory (void)
 {
   FILE *fmem;
-  unsigned long retval = 2 * 1024 * 1024; /* default: 2GB */
+  uintmax_t retval = 2 * 1024 * 1024; /* default: 2 GiB */
+  int c;
 
   block_input ();
   fmem = emacs_fopen ("/proc/meminfo", "r");
 
   if (fmem)
     {
-      unsigned long entry_value;
-      char entry_name[20];     /* the longest I saw is 13+1 */
+      uintmax_t entry_value;
+      bool done;
+
+      do
+       switch (fscanf (fmem, "MemTotal: %"SCNuMAX, &entry_value))
+         {
+         case 1:
+           retval = entry_value;
+           done = 1;
+           break;
+
+         case 0:
+           while ((c = getc (fmem)) != EOF && c != '\n')
+             continue;
+           done = c == EOF;
+           break;
+
+         default:
+           done = 1;
+           break;
+         }
+      while (!done);
 
-      while (!feof (fmem) && !ferror (fmem))
-       {
-         if (fscanf (fmem, "%s %lu kB\n", entry_name, &entry_value) >= 2
-             && strcmp (entry_name, "MemTotal:") == 0)
-           {
-             retval = entry_value;
-             break;
-           }
-       }
       fclose (fmem);
     }
   unblock_input ();
@@ -2896,7 +3054,7 @@ 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;
-  EMACS_TIME tnow, tstart, tboot, telapsed, us_time;
+  struct timespec tnow, tstart, tboot, telapsed, us_time;
   double pcpu, pmem;
   Lisp_Object attrs = Qnil;
   Lisp_Object cmd_str, decoded_cmd;
@@ -3017,20 +3175,19 @@ system_process_attributes (Lisp_Object pid)
          attrs = Fcons (Fcons (Qnice, make_number (niceness)), attrs);
          attrs = Fcons (Fcons (Qthcount, make_fixnum_or_float (thcount)),
                         attrs);
-         tnow = current_emacs_time ();
+         tnow = current_timespec ();
          telapsed = get_up_time ();
-         tboot = sub_emacs_time (tnow, telapsed);
+         tboot = timespec_sub (tnow, telapsed);
          tstart = time_from_jiffies (start, clocks_per_sec);
-         tstart = add_emacs_time (tboot, tstart);
+         tstart = timespec_add (tboot, tstart);
          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);
-         telapsed = sub_emacs_time (tnow, tstart);
+         telapsed = timespec_sub (tnow, tstart);
          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));
+         pcpu = timespectod (us_time) / timespectod (telapsed);
          if (pcpu > 1.0)
            pcpu = 1.0;
          attrs = Fcons (Fcons (Qpcpu, make_float (100 * pcpu)), attrs);
@@ -3248,16 +3405,16 @@ system_process_attributes (Lisp_Object pid)
 
 #elif defined __FreeBSD__
 
-static EMACS_TIME
-timeval_to_EMACS_TIME (struct timeval t)
+static struct timespec
+timeval_to_timespec (struct timeval t)
 {
-  return make_emacs_time (t.tv_sec, t.tv_usec * 1000);
+  return make_timespec (t.tv_sec, t.tv_usec * 1000);
 }
 
 static Lisp_Object
 make_lisp_timeval (struct timeval t)
 {
-  return make_lisp_time (timeval_to_EMACS_TIME (t));
+  return make_lisp_time (timeval_to_timespec (t));
 }
 
 Lisp_Object
@@ -3265,14 +3422,14 @@ system_process_attributes (Lisp_Object pid)
 {
   int proc_id;
   int pagesize = getpagesize ();
-  int npages;
+  unsigned long npages;
   int fscale;
   struct passwd *pw;
   struct group  *gr;
   char *ttyname;
   size_t len;
   char args[MAXPATHLEN];
-  EMACS_TIME t, now;
+  struct timespec t, now;
 
   int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID};
   struct kinfo_proc proc;
@@ -3359,8 +3516,8 @@ system_process_attributes (Lisp_Object pid)
                 attrs);
   attrs = Fcons (Fcons (Qstime, make_lisp_timeval (proc.ki_rusage.ru_stime)),
                 attrs);
-  t = add_emacs_time (timeval_to_EMACS_TIME (proc.ki_rusage.ru_utime),
-                     timeval_to_EMACS_TIME (proc.ki_rusage.ru_stime));
+  t = timespec_add (timeval_to_timespec (proc.ki_rusage.ru_utime),
+                   timeval_to_timespec (proc.ki_rusage.ru_stime));
   attrs = Fcons (Fcons (Qtime, make_lisp_time (t)), attrs);
 
   attrs = Fcons (Fcons (Qcutime,
@@ -3369,8 +3526,8 @@ system_process_attributes (Lisp_Object pid)
   attrs = Fcons (Fcons (Qcstime,
                        make_lisp_timeval (proc.ki_rusage_ch.ru_utime)),
                 attrs);
-  t = add_emacs_time (timeval_to_EMACS_TIME (proc.ki_rusage_ch.ru_utime),
-                     timeval_to_EMACS_TIME (proc.ki_rusage_ch.ru_stime));
+  t = timespec_add (timeval_to_timespec (proc.ki_rusage_ch.ru_utime),
+                   timeval_to_timespec (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)),
@@ -3382,8 +3539,8 @@ system_process_attributes (Lisp_Object pid)
   attrs = Fcons (Fcons (Qrss,   make_number (proc.ki_rssize * pagesize >> 10)),
                 attrs);
 
-  now = current_emacs_time ();
-  t = sub_emacs_time (now, timeval_to_EMACS_TIME (proc.ki_start));
+  now = current_timespec ();
+  t = timespec_sub (now, timeval_to_timespec (proc.ki_start));
   attrs = Fcons (Fcons (Qetime, make_lisp_time (t)), attrs);
 
   len = sizeof fscale;
@@ -3443,3 +3600,208 @@ system_process_attributes (Lisp_Object pid)
 }
 
 #endif /* !defined (WINDOWSNT) */
+\f
+/* Wide character string collation.  */
+
+#ifdef __STDC_ISO_10646__
+# include <wchar.h>
+# include <wctype.h>
+
+# if defined HAVE_NEWLOCALE || defined HAVE_SETLOCALE
+#  include <locale.h>
+# endif
+# ifndef LC_COLLATE
+#  define LC_COLLATE 0
+# endif
+# ifndef LC_COLLATE_MASK
+#  define LC_COLLATE_MASK 0
+# endif
+# ifndef LC_CTYPE
+#  define LC_CTYPE 0
+# endif
+# ifndef LC_CTYPE_MASK
+#  define LC_CTYPE_MASK 0
+# endif
+
+# ifndef HAVE_NEWLOCALE
+#  undef freelocale
+#  undef locale_t
+#  undef newlocale
+#  undef wcscoll_l
+#  undef towlower_l
+#  define freelocale emacs_freelocale
+#  define locale_t emacs_locale_t
+#  define newlocale emacs_newlocale
+#  define wcscoll_l emacs_wcscoll_l
+#  define towlower_l emacs_towlower_l
+
+typedef char const *locale_t;
+
+static locale_t
+newlocale (int category_mask, char const *locale, locale_t loc)
+{
+  return locale;
+}
+
+static void
+freelocale (locale_t loc)
+{
+}
+
+static char *
+emacs_setlocale (int category, char const *locale)
+{
+#  ifdef HAVE_SETLOCALE
+  errno = 0;
+  char *loc = setlocale (category, locale);
+  if (loc || errno)
+    return loc;
+  errno = EINVAL;
+#  else
+  errno = ENOTSUP;
+#  endif
+  return 0;
+}
+
+static int
+wcscoll_l (wchar_t const *a, wchar_t const *b, locale_t loc)
+{
+  int result = 0;
+  char *oldloc = emacs_setlocale (LC_COLLATE, NULL);
+  int err;
+
+  if (! oldloc)
+    err = errno;
+  else
+    {
+      USE_SAFE_ALLOCA;
+      char *oldcopy = SAFE_ALLOCA (strlen (oldloc) + 1);
+      strcpy (oldcopy, oldloc);
+      if (! emacs_setlocale (LC_COLLATE, loc))
+       err = errno;
+      else
+       {
+         errno = 0;
+         result = wcscoll (a, b);
+         err = errno;
+         if (! emacs_setlocale (LC_COLLATE, oldcopy))
+           err = errno;
+       }
+      SAFE_FREE ();
+    }
+
+  errno = err;
+  return result;
+}
+
+static wint_t
+towlower_l (wint_t wc, locale_t loc)
+{
+  wint_t result = wc;
+  char *oldloc = emacs_setlocale (LC_CTYPE, NULL);
+
+  if (oldloc)
+    {
+      USE_SAFE_ALLOCA;
+      char *oldcopy = SAFE_ALLOCA (strlen (oldloc) + 1);
+      strcpy (oldcopy, oldloc);
+      if (emacs_setlocale (LC_CTYPE, loc))
+       {
+         result = towlower (wc);
+         emacs_setlocale (LC_COLLATE, oldcopy);
+       }
+      SAFE_FREE ();
+    }
+
+  return result;
+}
+# endif
+
+int
+str_collate (Lisp_Object s1, Lisp_Object s2,
+            Lisp_Object locale, Lisp_Object ignore_case)
+{
+  int res, err;
+  ptrdiff_t len, i, i_byte;
+  wchar_t *p1, *p2;
+
+  USE_SAFE_ALLOCA;
+
+  /* Convert byte stream to code points.  */
+  len = SCHARS (s1); i = i_byte = 0;
+  SAFE_NALLOCA (p1, 1, len + 1);
+  while (i < len)
+    FETCH_STRING_CHAR_ADVANCE (*(p1+i-1), s1, i, i_byte);
+  *(p1+len) = 0;
+
+  len = SCHARS (s2); i = i_byte = 0;
+  SAFE_NALLOCA (p2, 1, len + 1);
+  while (i < len)
+    FETCH_STRING_CHAR_ADVANCE (*(p2+i-1), s2, i, i_byte);
+  *(p2+len) = 0;
+
+  if (STRINGP (locale))
+    {
+      locale_t loc = newlocale (LC_COLLATE_MASK | LC_CTYPE_MASK,
+                               SSDATA (locale), 0);
+      if (!loc)
+       error ("Invalid locale %s: %s", SSDATA (locale), strerror (errno));
+
+      if (! NILP (ignore_case))
+       for (int i = 1; i < 3; i++)
+         {
+           wchar_t *p = (i == 1) ? p1 : p2;
+           for (; *p; p++)
+             *p = towlower_l (*p, loc);
+         }
+
+      errno = 0;
+      res = wcscoll_l (p1, p2, loc);
+      err = errno;
+      freelocale (loc);
+    }
+  else
+    {
+      if (! NILP (ignore_case))
+       for (int i = 1; i < 3; i++)
+         {
+           wchar_t *p = (i == 1) ? p1 : p2;
+           for (; *p; p++)
+             *p = towlower (*p);
+         }
+
+      errno = 0;
+      res = wcscoll (p1, p2);
+      err = errno;
+    }
+#  ifndef HAVE_NEWLOCALE
+  if (err)
+    error ("Invalid locale or string for collation: %s", strerror (err));
+#  else
+  if (err)
+    error ("Invalid string for collation: %s", strerror (err));
+#  endif
+
+  SAFE_FREE ();
+  return res;
+}
+#endif  /* __STDC_ISO_10646__ */
+
+#ifdef WINDOWSNT
+int
+str_collate (Lisp_Object s1, Lisp_Object s2,
+            Lisp_Object locale, Lisp_Object ignore_case)
+{
+
+  char *loc = STRINGP (locale) ? SSDATA (locale) : NULL;
+  int res, err = errno;
+
+  errno = 0;
+  res = w32_compare_strings (SDATA (s1), SDATA (s2), loc, !NILP (ignore_case));
+  if (errno)
+    error ("Invalid string for collation: %s", strerror (errno));
+
+  errno = err;
+  return res;
+}
+#endif /* WINDOWSNT */