diff src/process.c @ 89909:68c22ea6027c

Sync to HEAD
author Kenichi Handa <handa@m17n.org>
date Fri, 16 Apr 2004 12:51:06 +0000
parents 8c02d5cc8f27
children ab6b6e8cffe6
line wrap: on
line diff
--- a/src/process.c	Thu Apr 15 01:08:34 2004 +0000
+++ b/src/process.c	Fri Apr 16 12:51:06 2004 +0000
@@ -1,6 +1,6 @@
 /* Asynchronous subprocess control for GNU Emacs.
    Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999,
-      2001, 2002, 2003 Free Software Foundation, Inc.
+      2001, 2002, 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -98,6 +98,17 @@
 #include <bsdtty.h>
 #endif
 
+/* Can we use SIOCGIFCONF and/or SIOCGIFADDR */
+#ifdef HAVE_SOCKETS
+#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_NET_IF_H)
+/* sys/ioctl.h may have been included already */
+#ifndef SIOCGIFADDR
+#include <sys/ioctl.h>
+#endif
+#include <net/if.h>
+#endif
+#endif
+
 #ifdef IRIS
 #include <sys/sysmacros.h>	/* for "minor" */
 #endif /* not IRIS */
@@ -249,6 +260,33 @@
 #undef DATAGRAM_SOCKETS
 #endif
 
+#if !defined (ADAPTIVE_READ_BUFFERING) && !defined (NO_ADAPTIVE_READ_BUFFERING)
+#ifdef EMACS_HAS_USECS
+#define ADAPTIVE_READ_BUFFERING
+#endif
+#endif
+
+#ifdef ADAPTIVE_READ_BUFFERING
+#define READ_OUTPUT_DELAY_INCREMENT 10000
+#define READ_OUTPUT_DELAY_MAX       (READ_OUTPUT_DELAY_INCREMENT * 5)
+#define READ_OUTPUT_DELAY_MAX_MAX   (READ_OUTPUT_DELAY_INCREMENT * 7)
+
+/* Number of processes which might be delayed.  */
+
+static int process_output_delay_count;
+
+/* Non-zero if any process has non-nil process_output_skip.  */
+
+static int process_output_skip;
+
+/* Non-nil means to delay reading process output to improve buffering.
+   A value of t means that delay is reset after each send, any other
+   non-nil value does not reset the delay.  */
+static Lisp_Object Vprocess_adaptive_read_buffering;
+#else
+#define process_output_delay_count 0
+#endif
+
 
 #include "sysselect.h"
 
@@ -562,6 +600,12 @@
   p->status = Qrun;
   p->mark = Fmake_marker ();
 
+#ifdef ADAPTIVE_READ_BUFFERING
+  p->adaptive_read_buffering = Qnil;
+  XSETFASTINT (p->read_output_delay, 0);
+  p->read_output_skip = Qnil;
+#endif
+
   /* If name is already in use, modify it until it is unused.  */
 
   name1 = name;
@@ -955,8 +999,14 @@
      (process, sentinel)
      register Lisp_Object process, sentinel;
 {
+  struct Lisp_Process *p;
+
   CHECK_PROCESS (process);
-  XPROCESS (process)->sentinel = sentinel;
+  p = XPROCESS (process);
+
+  p->sentinel = sentinel;
+  if (NETCONN1_P (p))
+    p->childp = Fplist_put (p->childp, QCsentinel, sentinel);
   return sentinel;
 }
 
@@ -1485,6 +1535,10 @@
     = buffer_defaults.enable_multibyte_characters;
   XPROCESS (proc)->command = Flist (nargs - 2, args + 2);
 
+#ifdef ADAPTIVE_READ_BUFFERING
+  XPROCESS (proc)->adaptive_read_buffering = Vprocess_adaptive_read_buffering;
+#endif
+
   /* Make the process marker point into the process buffer (if any).  */
   if (!NILP (buffer))
     set_marker_both (XPROCESS (proc)->mark, buffer,
@@ -2298,233 +2352,172 @@
   /* The name of this option.  Should be lowercase version of option
      name without SO_ prefix. */
   char *name;
-  /* Length of name.  */
-  int nlen;
   /* Option level SOL_... */
   int optlevel;
   /* Option number SO_... */
   int optnum;
-  enum { SOPT_UNKNOWN, SOPT_BOOL, SOPT_INT, SOPT_STR, SOPT_LINGER } opttype;
+  enum { SOPT_UNKNOWN, SOPT_BOOL, SOPT_INT, SOPT_IFNAME, SOPT_LINGER } opttype;
+  enum { OPIX_NONE=0, OPIX_MISC=1, OPIX_REUSEADDR=2 } optbit;
 } socket_options[] =
   {
 #ifdef SO_BINDTODEVICE
-    { "bindtodevice", 12, SOL_SOCKET, SO_BINDTODEVICE, SOPT_STR },
+    { ":bindtodevice", SOL_SOCKET, SO_BINDTODEVICE, SOPT_IFNAME, OPIX_MISC },
 #endif
 #ifdef SO_BROADCAST
-    { "broadcast", 9, SOL_SOCKET, SO_BROADCAST, SOPT_BOOL },
+    { ":broadcast", SOL_SOCKET, SO_BROADCAST, SOPT_BOOL, OPIX_MISC },
 #endif
 #ifdef SO_DONTROUTE
-    { "dontroute", 9, SOL_SOCKET, SO_DONTROUTE, SOPT_BOOL },
+    { ":dontroute", SOL_SOCKET, SO_DONTROUTE, SOPT_BOOL, OPIX_MISC },
 #endif
 #ifdef SO_KEEPALIVE
-    { "keepalive", 9, SOL_SOCKET, SO_KEEPALIVE, SOPT_BOOL },
+    { ":keepalive", SOL_SOCKET, SO_KEEPALIVE, SOPT_BOOL, OPIX_MISC },
 #endif
 #ifdef SO_LINGER
-    { "linger", 6, SOL_SOCKET, SO_LINGER, SOPT_LINGER },
+    { ":linger", SOL_SOCKET, SO_LINGER, SOPT_LINGER, OPIX_MISC },
 #endif
 #ifdef SO_OOBINLINE
-    { "oobinline", 9, SOL_SOCKET, SO_OOBINLINE, SOPT_BOOL },
+    { ":oobinline", SOL_SOCKET, SO_OOBINLINE, SOPT_BOOL, OPIX_MISC },
 #endif
 #ifdef SO_PRIORITY
-    { "priority", 8, SOL_SOCKET, SO_PRIORITY, SOPT_INT },
+    { ":priority", SOL_SOCKET, SO_PRIORITY, SOPT_INT, OPIX_MISC },
 #endif
 #ifdef SO_REUSEADDR
-    { "reuseaddr", 9, SOL_SOCKET, SO_REUSEADDR, SOPT_BOOL },
-#endif
-    { 0, 0, 0, 0, SOPT_UNKNOWN }
+    { ":reuseaddr", SOL_SOCKET, SO_REUSEADDR, SOPT_BOOL, OPIX_REUSEADDR },
+#endif
+    { 0, 0, 0, SOPT_UNKNOWN, OPIX_NONE }
   };
 
-/* Process list of socket options OPTS on socket S.
-   Only check if options are supported is S < 0.
-   If NO_ERROR is non-zero, continue silently if an option
-   cannot be set.
-
-   Each element specifies one option.  An element is either a string
-   "OPTION=VALUE" or a cons (OPTION . VALUE) where OPTION is a string
-   or a symbol.  */
+/* Set option OPT to value VAL on socket S.
+
+   Returns (1<<socket_options[OPT].optbit) if option is known, 0 otherwise.
+   Signals an error if setting a known option fails.
+*/
 
 static int
-set_socket_options (s, opts, no_error)
+set_socket_option (s, opt, val)
      int s;
-     Lisp_Object opts;
-     int no_error;
+     Lisp_Object opt, val;
 {
-  if (!CONSP (opts))
-    opts = Fcons (opts, Qnil);
-
-  while (CONSP (opts))
+  char *name;
+  struct socket_options *sopt;
+  int ret = 0;
+
+  CHECK_SYMBOL (opt);
+
+  name = (char *) SDATA (SYMBOL_NAME (opt));
+  for (sopt = socket_options; sopt->name; sopt++)
+    if (strcmp (name, sopt->name) == 0)
+      break;
+
+  switch (sopt->opttype)
     {
-      Lisp_Object opt;
-      Lisp_Object val;
-      char *name, *arg;
-      struct socket_options *sopt;
-      int ret = 0;
-
-      opt = XCAR (opts);
-      opts = XCDR (opts);
-
-      name = 0;
-      val = Qt;
-      if (CONSP (opt))
-	{
-	  val = XCDR (opt);
-	  opt = XCAR (opt);
-	}
-      if (STRINGP (opt))
-	name = (char *) SDATA (opt);
-      else if (SYMBOLP (opt))
-	name = (char *) SDATA (SYMBOL_NAME (opt));
-      else {
-	error ("Mal-formed option list");
-	return 0;
+    case SOPT_BOOL:
+      {
+	int optval;
+	optval = NILP (val) ? 0 : 1;
+	ret = setsockopt (s, sopt->optlevel, sopt->optnum,
+			  &optval, sizeof (optval));
+	break;
       }
 
-      if (strncmp (name, "no", 2) == 0)
-	{
-	  val = Qnil;
-	  name += 2;
-	}
-
-      arg = 0;
-      for (sopt = socket_options; sopt->name; sopt++)
-	if (strncmp (name, sopt->name, sopt->nlen) == 0)
+    case SOPT_INT:
+      {
+	int optval;
+	if (INTEGERP (val))
+	  optval = XINT (val);
+	else
+	  error ("Bad option value for %s", name);
+	ret = setsockopt (s, sopt->optlevel, sopt->optnum,
+			  &optval, sizeof (optval));
+	break;
+      }
+
+#ifdef SO_BINDTODEVICE
+    case SOPT_IFNAME:
+      {
+	char devname[IFNAMSIZ+1];
+
+	/* This is broken, at least in the Linux 2.4 kernel.
+	   To unbind, the arg must be a zero integer, not the empty string.
+	   This should work on all systems.   KFS. 2003-09-23.  */
+	bzero (devname, sizeof devname);
+	if (STRINGP (val))
 	  {
-	    if (name[sopt->nlen] == 0)
-	      break;
-	    if (name[sopt->nlen] == '=')
-	      {
-		arg = name + sopt->nlen + 1;
-		break;
-	      }
-	  }
-
-      switch (sopt->opttype)
-	{
-	case SOPT_BOOL:
-	  {
-	    int optval;
-	    if (s < 0)
-	      return 1;
-	    if (arg)
-	      optval = (*arg == '0' || *arg == 'n') ? 0 : 1;
-	    else if (INTEGERP (val))
-	      optval = XINT (val) == 0 ? 0 : 1;
-	    else
-	      optval = NILP (val) ? 0 : 1;
-	    ret = setsockopt (s, sopt->optlevel, sopt->optnum,
-			      &optval, sizeof (optval));
-	    break;
+	    char *arg = (char *) SDATA (val);
+	    int len = min (strlen (arg), IFNAMSIZ);
+	    bcopy (arg, devname, len);
 	  }
-
-	case SOPT_INT:
-	  {
-	    int optval;
-	    if (arg)
-	      optval = atoi(arg);
-	    else if (INTEGERP (val))
-	      optval = XINT (val);
-	    else
-	      error ("Bad option argument for %s", name);
-	    if (s < 0)
-	      return 1;
-	    ret = setsockopt (s, sopt->optlevel, sopt->optnum,
-			      &optval, sizeof (optval));
-	    break;
-	  }
-
-	case SOPT_STR:
-	  {
-	    if (!arg)
-	      {
-		if (NILP (val))
-		  arg = "";
-		else if (STRINGP (val))
-		  arg = (char *) SDATA (val);
-		else if (XSYMBOL (val))
-		  arg = (char *) SDATA (SYMBOL_NAME (val));
-		else
-		  error ("Invalid argument to %s option", name);
-	      }
-	    ret = setsockopt (s, sopt->optlevel, sopt->optnum,
-			      arg, strlen (arg));
-	  }
+	else if (!NILP (val))
+	  error ("Bad option value for %s", name);
+	ret = setsockopt (s, sopt->optlevel, sopt->optnum,
+			  devname, IFNAMSIZ);
+	break;
+      }
+#endif
 
 #ifdef SO_LINGER
-	case SOPT_LINGER:
-	  {
-	    struct linger linger;
-
-	    linger.l_onoff = 1;
-	    linger.l_linger = 0;
-
-	    if (s < 0)
-	      return 1;
-
-	    if (arg)
-	      {
-		if (*arg == 'n' || *arg == 't' || *arg == 'y')
-		  linger.l_onoff = (*arg == 'n') ? 0 : 1;
-		else
-		  linger.l_linger = atoi(arg);
-	      }
-	    else if (INTEGERP (val))
-	      linger.l_linger = XINT (val);
-	    else
-	      linger.l_onoff = NILP (val) ? 0 : 1;
-	    ret = setsockopt (s, sopt->optlevel, sopt->optnum,
-			      &linger, sizeof (linger));
-	    break;
-	  }
-#endif
-	default:
-	  if (s < 0)
-	    return 0;
-	  if (no_error)
-	    continue;
-	  error ("Unsupported option: %s", name);
-	}
-      if (ret < 0 && ! no_error)
-	  report_file_error ("Cannot set network option: %s", opt);
+    case SOPT_LINGER:
+      {
+	struct linger linger;
+
+	linger.l_onoff = 1;
+	linger.l_linger = 0;
+	if (INTEGERP (val))
+	  linger.l_linger = XINT (val);
+	else
+	  linger.l_onoff = NILP (val) ? 0 : 1;
+	ret = setsockopt (s, sopt->optlevel, sopt->optnum,
+			  &linger, sizeof (linger));
+	break;
+      }
+#endif
+
+    default:
+      return 0;
     }
-  return 1;
+
+  if (ret < 0)
+    report_file_error ("Cannot set network option",
+		       Fcons (opt, Fcons (val, Qnil)));
+  return (1 << sopt->optbit);
 }
 
-DEFUN ("set-network-process-options",
-       Fset_network_process_options, Sset_network_process_options,
-       1, MANY, 0,
-       doc: /* Set one or more options for network process PROCESS.
-Each option is either a string "OPT=VALUE" or a cons (OPT . VALUE).
-A boolean value is false if it either zero or nil, true otherwise.
-
-The following options are known.  Consult the relevant system manual
-pages for more information.
-
-bindtodevice=NAME -- bind to interface NAME, or remove binding if nil.
-broadcast=BOOL -- Allow send and receive of datagram broadcasts.
-dontroute=BOOL -- Only send to directly connected hosts.
-keepalive=BOOL -- Send keep-alive messages on network stream.
-linger=BOOL or TIMEOUT -- Send queued messages before closing.
-oobinline=BOOL -- Place out-of-band data in receive data stream.
-priority=INT -- Set protocol defined priority for sent packets.
-reuseaddr=BOOL -- Allow reusing a recently used address.
-
-usage: (set-network-process-options PROCESS &rest OPTIONS)  */)
-     (nargs, args)
-     int nargs;
-     Lisp_Object *args;
+
+DEFUN ("set-network-process-option",
+       Fset_network_process_option, Sset_network_process_option,
+       3, 4, 0,
+       doc: /* For network process PROCESS set option OPTION to value VALUE.
+See `make-network-process' for a list of options and values.
+If optional fourth arg NO-ERROR is non-nil, don't signal an error if
+OPTION is not a supported option, return nil instead; otherwise return t.  */)
+     (process, option, value, no_error)
+     Lisp_Object process, option, value;
+     Lisp_Object no_error;
 {
-  Lisp_Object process;
-  Lisp_Object opts;
-
-  process = args[0];
+  int s;
+  struct Lisp_Process *p;
+
   CHECK_PROCESS (process);
-  if (nargs > 1 && XINT (XPROCESS (process)->infd) >= 0)
+  p = XPROCESS (process);
+  if (!NETCONN1_P (p))
+    error ("Process is not a network process");
+
+  s = XINT (p->infd);
+  if (s < 0)
+    error ("Process is not running");
+
+  if (set_socket_option (s, option, value))
     {
-      opts = Flist (nargs, args);
-      set_socket_options (XINT (XPROCESS (process)->infd), opts, 0);
+      p->childp = Fplist_put (p->childp, option, value);
+      return Qt;
     }
-  return process;
+
+  if (NILP (no_error))
+    error ("Unknown or unsupported option");
+
+  return Qnil;
 }
+
 
 /* A version of request_sigio suitable for a record_unwind_protect.  */
 
@@ -2604,10 +2597,10 @@
 this format in portable code, as it may depend on implementation
 defined constants, data sizes, and data structure alignment.
 
-:coding CODING -- CODING is coding system for this process.
-
-:options OPTIONS -- Set the specified options for the network process.
-See `set-network-process-options' for details.
+:coding CODING -- If CODING is a symbol, it specifies the coding
+system used for both reading and writing for this process.  If CODING
+is a cons (DECODING . ENCODING), DECODING is used for reading, and
+ENCODING is used for writing.
 
 :nowait BOOL -- If BOOL is non-nil for a stream type client process,
 return without waiting for the connection to complete; instead, the
@@ -2641,13 +2634,33 @@
 
 :plist PLIST -- Install PLIST as the new process' initial plist.
 
-:server BOOL -- if BOOL is non-nil, create a server process for the
+:server QLEN -- if QLEN is non-nil, create a server process for the
 specified FAMILY, SERVICE, and connection type (stream or datagram).
-Default is a client process.
-
-A server process will listen for and accept connections from
-clients.  When a client connection is accepted, a new network process
-is created for the connection with the following parameters:
+If QLEN is an integer, it is used as the max. length of the server's
+pending connection queue (also known as the backlog); the default
+queue length is 5.  Default is to create a client process.
+
+The following network options can be specified for this connection:
+
+:broadcast BOOL    -- Allow send and receive of datagram broadcasts.
+:dontroute BOOL    -- Only send to directly connected hosts.
+:keepalive BOOL    -- Send keep-alive messages on network stream.
+:linger BOOL or TIMEOUT -- Send queued messages before closing.
+:oobinline BOOL    -- Place out-of-band data in receive data stream.
+:priority INT      -- Set protocol defined priority for sent packets.
+:reuseaddr BOOL    -- Allow reusing a recently used local address
+                      (this is allowed by default for a server process).
+:bindtodevice NAME -- bind to interface NAME.  Using this may require
+                      special privileges on some systems.
+
+Consult the relevant system programmer's manual pages for more
+information on using these options.
+
+
+A server process will listen for and accept connections from clients.
+When a client connection is accepted, a new network process is created
+for the connection with the following parameters:
+
 - The client's process name is constructed by concatenating the server
 process' NAME and a client identification string.
 - If the FILTER argument is non-nil, the client process will not get a
@@ -2708,7 +2721,7 @@
   Lisp_Object name, buffer, host, service, address;
   Lisp_Object filter, sentinel;
   int is_non_blocking_client = 0;
-  int is_server = 0;
+  int is_server = 0, backlog = 5;
   int socktype;
   int family = -1;
 
@@ -2745,6 +2758,8 @@
       error ("Network servers not supported");
 #else
       is_server = 1;
+      if (INTEGERP (tem))
+	backlog = XINT (tem);
 #endif
     }
 
@@ -2997,6 +3012,8 @@
 
   for (lres = res; lres; lres = lres->ai_next)
     {
+      int optn, optbits;
+
       s = socket (lres->ai_family, lres->ai_socktype, lres->ai_protocol);
       if (s < 0)
 	{
@@ -3030,17 +3047,27 @@
       /* Make us close S if quit.  */
       record_unwind_protect (close_file_unwind, make_number (s));
 
+      /* Parse network options in the arg list.
+	 We simply ignore anything which isn't a known option (including other keywords).
+         An error is signalled if setting a known option fails.  */
+      for (optn = optbits = 0; optn < nargs-1; optn += 2)
+	optbits |= set_socket_option (s, args[optn], args[optn+1]);
+
       if (is_server)
 	{
 	  /* Configure as a server socket.  */
+
+	  /* SO_REUSEADDR = 1 is default for server sockets; must specify
+	     explicit :reuseaddr key to override this.  */
 #ifdef HAVE_LOCAL_SOCKETS
 	  if (family != AF_LOCAL)
 #endif
-	    {
-	      int optval = 1;
-	      if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
-		report_file_error ("Cannot set reuse option on server socket.", Qnil);
-	    }
+	    if (!(optbits & (1 << OPIX_REUSEADDR)))
+	      {
+		int optval = 1;
+		if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval))
+		  report_file_error ("Cannot set reuse option on server socket", Qnil);
+	      }
 
 	  if (bind (s, lres->ai_addr, lres->ai_addrlen))
 	    report_file_error ("Cannot bind server socket", Qnil);
@@ -3059,7 +3086,7 @@
 	    }
 #endif
 
-	  if (socktype == SOCK_STREAM && listen (s, 5))
+	  if (socktype == SOCK_STREAM && listen (s, backlog))
 	    report_file_error ("Cannot listen on server socket", Qnil);
 
 	  break;
@@ -3195,10 +3222,6 @@
 	report_file_error ("make client process failed", contact);
     }
 
-  tem = Fplist_get (contact, QCoptions);
-  if (!NILP (tem))
-    set_socket_options (s, tem, 1);
-
 #endif /* not TERM */
 
   inch = s;
@@ -3280,7 +3303,11 @@
     Lisp_Object args[5], val;
 
     if (!NILP (tem))
-      val = XCAR (XCDR (tem));
+      {
+	val = XCAR (XCDR (tem));
+	if (CONSP (val))
+	  val = XCAR (val);
+      }
     else if (!NILP (Vcoding_system_for_read))
       val = Vcoding_system_for_read;
     else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters))
@@ -3312,7 +3339,11 @@
     p->decode_coding_system = val;
 
     if (!NILP (tem))
-      val = XCAR (XCDR (tem));
+      {
+	val = XCAR (XCDR (tem));
+	if (CONSP (val))
+	  val = XCDR (val);
+      }
     else if (!NILP (Vcoding_system_for_write))
       val = Vcoding_system_for_write;
     else if (NILP (current_buffer->enable_multibyte_characters))
@@ -3357,6 +3388,234 @@
 }
 #endif	/* HAVE_SOCKETS */
 
+
+#if defined(HAVE_SOCKETS) && defined(HAVE_NET_IF_H) && defined(HAVE_SYS_IOCTL_H)
+
+#ifdef SIOCGIFCONF
+DEFUN ("network-interface-list", Fnetwork_interface_list, Snetwork_interface_list, 0, 0, 0,
+       doc: /* Return an alist of all network interfaces and their network address.
+Each element is a cons, the car of which is a string containing the
+interface name, and the cdr is the network address in internal
+format; see the description of ADDRESS in `make-network-process'.  */)
+     ()
+{
+  struct ifconf ifconf;
+  struct ifreq *ifreqs = NULL;
+  int ifaces = 0;
+  int buf_size, s;
+  Lisp_Object res;
+
+  s = socket (AF_INET, SOCK_STREAM, 0);
+  if (s < 0)
+    return Qnil;
+
+ again:
+  ifaces += 25;
+  buf_size = ifaces * sizeof(ifreqs[0]);
+  ifreqs = (struct ifreq *)xrealloc(ifreqs, buf_size);
+  if (!ifreqs)
+    {
+      close (s);
+      return Qnil;
+    }
+
+  ifconf.ifc_len = buf_size;
+  ifconf.ifc_req = ifreqs;
+  if (ioctl (s, SIOCGIFCONF, &ifconf))
+    {
+      close (s);
+      return Qnil;
+    }
+
+  if (ifconf.ifc_len == buf_size)
+    goto again;
+
+  close (s);
+  ifaces = ifconf.ifc_len / sizeof (ifreqs[0]);
+
+  res = Qnil;
+  while (--ifaces >= 0)
+    {
+      struct ifreq *ifq = &ifreqs[ifaces];
+      char namebuf[sizeof (ifq->ifr_name) + 1];
+      if (ifq->ifr_addr.sa_family != AF_INET)
+	continue;
+      bcopy (ifq->ifr_name, namebuf, sizeof (ifq->ifr_name));
+      namebuf[sizeof (ifq->ifr_name)] = 0;
+      res = Fcons (Fcons (build_string (namebuf),
+			  conv_sockaddr_to_lisp (&ifq->ifr_addr,
+						 sizeof (struct sockaddr))),
+		   res);
+    }
+
+  return res;
+}
+#endif /* SIOCGIFCONF */
+
+#if defined(SIOCGIFADDR) || defined(SIOCGIFHWADDR) || defined(SIOCGIFFLAGS)
+
+struct ifflag_def {
+  int flag_bit;
+  char *flag_sym;
+};
+
+static struct ifflag_def ifflag_table[] = {
+#ifdef IFF_UP
+  { IFF_UP,		"up" },
+#endif
+#ifdef IFF_BROADCAST
+  { IFF_BROADCAST,	"broadcast" },
+#endif
+#ifdef IFF_DEBUG
+  { IFF_DEBUG,		"debug" },
+#endif
+#ifdef IFF_LOOPBACK
+  { IFF_LOOPBACK,	"loopback" },
+#endif
+#ifdef IFF_POINTOPOINT
+  { IFF_POINTOPOINT,	"pointopoint" },
+#endif
+#ifdef IFF_RUNNING
+  { IFF_RUNNING,	"running" },
+#endif
+#ifdef IFF_NOARP
+  { IFF_NOARP,		"noarp" },
+#endif
+#ifdef IFF_PROMISC
+  { IFF_PROMISC,	"promisc" },
+#endif
+#ifdef IFF_NOTRAILERS
+  { IFF_NOTRAILERS,	"notrailers" },
+#endif
+#ifdef IFF_ALLMULTI
+  { IFF_ALLMULTI,	"allmulti" },
+#endif
+#ifdef IFF_MASTER
+  { IFF_MASTER,		"master" },
+#endif
+#ifdef IFF_SLAVE
+  { IFF_SLAVE,		"slave" },
+#endif
+#ifdef IFF_MULTICAST
+  { IFF_MULTICAST,	"multicast" },
+#endif
+#ifdef IFF_PORTSEL
+  { IFF_PORTSEL,	"portsel" },
+#endif
+#ifdef IFF_AUTOMEDIA
+  { IFF_AUTOMEDIA,	"automedia" },
+#endif
+#ifdef IFF_DYNAMIC
+  { IFF_DYNAMIC,	"dynamic" },
+#endif
+  { 0, 0 }
+};
+
+DEFUN ("network-interface-info", Fnetwork_interface_info, Snetwork_interface_info, 1, 1, 0,
+       doc: /* Return information about network interface named IFNAME.
+The return value is a list (ADDR BCAST NETMASK HWADDR FLAGS),
+where ADDR is the layer 3 address, BCAST is the layer 3 broadcast address,
+NETMASK is the layer 3 network mask, HWADDR is the layer 2 addres, and
+FLAGS is the current flags of the interface.  */)
+     (ifname)
+     Lisp_Object ifname;
+{
+  struct ifreq rq;
+  Lisp_Object res = Qnil;
+  Lisp_Object elt;
+  int s;
+  int any = 0;
+
+  CHECK_STRING (ifname);
+
+  bzero (rq.ifr_name, sizeof rq.ifr_name);
+  strncpy (rq.ifr_name, SDATA (ifname), sizeof (rq.ifr_name));
+
+  s = socket (AF_INET, SOCK_STREAM, 0);
+  if (s < 0)
+    return Qnil;
+
+  elt = Qnil;
+#if defined(SIOCGIFFLAGS) && defined(HAVE_STRUCT_IFREQ_IFR_FLAGS)
+  if (ioctl (s, SIOCGIFFLAGS, &rq) == 0)
+    {
+      int flags = rq.ifr_flags;
+      struct ifflag_def *fp;
+      int fnum;
+
+      any++;
+      for (fp = ifflag_table; flags != 0 && fp; fp++)
+	{
+	  if (flags & fp->flag_bit)
+	    {
+	      elt = Fcons (intern (fp->flag_sym), elt);
+	      flags -= fp->flag_bit;
+	    }
+	}
+      for (fnum = 0; flags && fnum < 32; fnum++)
+	{
+	  if (flags & (1 << fnum))
+	    {
+	      elt = Fcons (make_number (fnum), elt);
+	    }
+	}
+    }
+#endif
+  res = Fcons (elt, res);
+
+  elt = Qnil;
+#if defined(SIOCGIFHWADDR) && defined(HAVE_STRUCT_IFREQ_IFR_HWADDR)
+  if (ioctl (s, SIOCGIFHWADDR, &rq) == 0)
+    {
+      Lisp_Object hwaddr = Fmake_vector (make_number (6), Qnil);
+      register struct Lisp_Vector *p = XVECTOR (hwaddr);
+      int n;
+
+      any++;
+      for (n = 0; n < 6; n++)
+	p->contents[n] = make_number (((unsigned char *)&rq.ifr_hwaddr.sa_data[0])[n]);
+      elt = Fcons (make_number (rq.ifr_hwaddr.sa_family), hwaddr);
+    }
+#endif
+  res = Fcons (elt, res);
+
+  elt = Qnil;
+#if defined(SIOCGIFNETMASK) && defined(ifr_netmask)
+  if (ioctl (s, SIOCGIFNETMASK, &rq) == 0)
+    {
+      any++;
+      elt = conv_sockaddr_to_lisp (&rq.ifr_netmask, sizeof (rq.ifr_netmask));
+    }
+#endif
+  res = Fcons (elt, res);
+
+  elt = Qnil;
+#if defined(SIOCGIFBRDADDR) && defined(HAVE_STRUCT_IFREQ_IFR_BROADADDR)
+  if (ioctl (s, SIOCGIFBRDADDR, &rq) == 0)
+    {
+      any++;
+      elt = conv_sockaddr_to_lisp (&rq.ifr_broadaddr, sizeof (rq.ifr_broadaddr));
+    }
+#endif
+  res = Fcons (elt, res);
+
+  elt = Qnil;
+#if defined(SIOCGIFADDR) && defined(HAVE_STRUCT_IFREQ_IFR_ADDR)
+  if (ioctl (s, SIOCGIFADDR, &rq) == 0)
+    {
+      any++;
+      elt = conv_sockaddr_to_lisp (&rq.ifr_addr, sizeof (rq.ifr_addr));
+    }
+#endif
+  res = Fcons (elt, res);
+
+  close (s);
+
+  return any ? res : Qnil;
+}
+#endif
+#endif	/* HAVE_SOCKETS */
+
 void
 deactivate_process (proc)
      Lisp_Object proc;
@@ -3367,6 +3626,16 @@
   inchannel = XINT (p->infd);
   outchannel = XINT (p->outfd);
 
+#ifdef ADAPTIVE_READ_BUFFERING
+  if (XINT (p->read_output_delay) > 0)
+    {
+      if (--process_output_delay_count < 0)
+	process_output_delay_count = 0;
+      XSETINT (p->read_output_delay, 0);
+      p->read_output_skip = Qnil;
+    }
+#endif
+      
   if (inchannel >= 0)
     {
       /* Beware SIGCHLD hereabouts. */
@@ -3752,7 +4021,7 @@
   register int channel, nfds;
   static SELECT_TYPE Available;
   static SELECT_TYPE Connecting;
-  int check_connect, no_avail;
+  int check_connect, check_delay, no_avail;
   int xerrno;
   Lisp_Object proc;
   EMACS_TIME timeout, end_time;
@@ -3812,6 +4081,10 @@
 	 Otherwise, do pending quit if requested.  */
       if (XINT (read_kbd) >= 0)
 	QUIT;
+#ifdef SYNC_INPUT
+      else if (interrupt_input_pending)
+	handle_async_input ();
+#endif
 
       /* Exit now if the cell we're waiting for became non-nil.  */
       if (! NILP (wait_for_cell) && ! NILP (XCAR (wait_for_cell)))
@@ -3981,7 +4254,7 @@
       if (!NILP (wait_for_cell))
 	{
 	  Available = non_process_wait_mask;
-	  check_connect = 0;
+	  check_connect = check_delay = 0;
 	}
       else
 	{
@@ -3990,6 +4263,7 @@
 	  else
 	    Available = input_wait_mask;
 	  check_connect = (num_pending_connects > 0);
+ 	  check_delay = wait_channel >= 0 ? 0 : process_output_delay_count;
 	}
 
       /* If frame size has changed or the window is newly mapped,
@@ -4015,6 +4289,34 @@
 	{
 	  if (check_connect)
 	    Connecting = connect_wait_mask;
+
+#ifdef ADAPTIVE_READ_BUFFERING
+	  if (process_output_skip && check_delay > 0)
+	    {
+	      int usecs = EMACS_USECS (timeout);
+	      if (EMACS_SECS (timeout) > 0 || usecs > READ_OUTPUT_DELAY_MAX)
+		usecs = READ_OUTPUT_DELAY_MAX;
+	      for (channel = 0; check_delay > 0 && channel <= max_process_desc; channel++)
+		{
+		  proc = chan_process[channel];
+		  if (NILP (proc))
+		    continue;
+		  if (XINT (XPROCESS (proc)->read_output_delay) > 0)
+		    {
+		      check_delay--;
+		      if (NILP (XPROCESS (proc)->read_output_skip))
+			continue;
+		      FD_CLR (channel, &Available);
+		      XPROCESS (proc)->read_output_skip = Qnil;
+		      if (XINT (XPROCESS (proc)->read_output_delay) < usecs)
+			usecs = XINT (XPROCESS (proc)->read_output_delay);
+		    }
+		}
+	      EMACS_SET_SECS_USECS (timeout, 0, usecs);
+	      process_output_skip = 0;
+	    }
+#endif
+
 	  nfds = select (max (max_process_desc, max_keyboard_desc) + 1,
 			 &Available,
 			 (check_connect ? &Connecting : (SELECT_TYPE *)0),
@@ -4468,7 +4770,36 @@
   else
 #endif
   if (proc_buffered_char[channel] < 0)
-    nbytes = emacs_read (channel, chars + carryover, readmax - carryover);
+    {
+      nbytes = emacs_read (channel, chars + carryover, readmax - carryover);
+#ifdef ADAPTIVE_READ_BUFFERING
+      if (!NILP (p->adaptive_read_buffering))
+	{
+	  int delay = XINT (p->read_output_delay);
+	  if (nbytes < 256)
+	    {
+	      if (delay < READ_OUTPUT_DELAY_MAX_MAX)
+		{
+		  if (delay == 0)
+		    process_output_delay_count++;
+		  delay += READ_OUTPUT_DELAY_INCREMENT * 2;
+		}
+	    }
+	  else if (delay > 0 && (nbytes == readmax - carryover))
+	    {
+	      delay -= READ_OUTPUT_DELAY_INCREMENT;
+	      if (delay == 0)
+		process_output_delay_count--;
+	    }
+	  XSETINT (p->read_output_delay, delay);
+	  if (delay)
+	    {
+	      p->read_output_skip = Qt;
+	      process_output_skip = 1;
+	    }
+	}
+#endif
+    }
   else
     {
       chars[carryover] = proc_buffered_char[channel];
@@ -4774,6 +5105,7 @@
      volatile Lisp_Object object;
 {
   /* Use volatile to protect variables from being clobbered by longjmp.  */
+  struct Lisp_Process *p = XPROCESS (proc);
   int rv;
   struct coding_system *coding;
   struct gcpro gcpro1;
@@ -4781,20 +5113,17 @@
   GCPRO1 (object);
 
 #ifdef VMS
-  struct Lisp_Process *p = XPROCESS (proc);
   VMS_PROC_STUFF *vs, *get_vms_process_pointer();
 #endif /* VMS */
 
-  if (! NILP (XPROCESS (proc)->raw_status_low))
-    update_status (XPROCESS (proc));
-  if (! EQ (XPROCESS (proc)->status, Qrun))
-    error ("Process %s not running",
-	   SDATA (XPROCESS (proc)->name));
-  if (XINT (XPROCESS (proc)->outfd) < 0)
-    error ("Output file descriptor of %s is closed",
-	   SDATA (XPROCESS (proc)->name));
-
-  coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)];
+  if (! NILP (p->raw_status_low))
+    update_status (p);
+  if (! EQ (p->status, Qrun))
+    error ("Process %s not running", SDATA (p->name));
+  if (XINT (p->outfd) < 0)
+    error ("Output file descriptor of %s is closed", SDATA (p->name));
+
+  coding = proc_encode_coding_system[XINT (p->outfd)];
   Vlast_coding_system_used = CODING_ID_NAME (coding->id);
 
   if ((STRINGP (object) && STRING_MULTIBYTE (object))
@@ -4802,14 +5131,12 @@
 	  && !NILP (XBUFFER (object)->enable_multibyte_characters))
       || EQ (object, Qt))
     {
-      if (!EQ (Vlast_coding_system_used,
-	       XPROCESS (proc)->encode_coding_system))
+      if (!EQ (Vlast_coding_system_used, p->encode_coding_system))
 	/* The coding system for encoding was changed to raw-text
 	   because we sent a unibyte text previously.  Now we are
 	   sending a multibyte text, thus we must encode it by the
-	   original coding system specified for the current
-	   process.  */
-	setup_coding_system (XPROCESS (proc)->encode_coding_system, coding);
+	   original coding system specified for the current process.  */
+	setup_coding_system (p->encode_coding_system, coding);
       coding->src_multibyte = 1;
     }
   else
@@ -4880,8 +5207,7 @@
   if (pty_max_bytes == 0)
     {
 #if defined (HAVE_FPATHCONF) && defined (_PC_MAX_CANON)
-      pty_max_bytes = fpathconf (XFASTINT (XPROCESS (proc)->outfd),
-				 _PC_MAX_CANON);
+      pty_max_bytes = fpathconf (XFASTINT (p->outfd), _PC_MAX_CANON);
       if (pty_max_bytes < 0)
 	pty_max_bytes = 250;
 #else
@@ -4904,7 +5230,7 @@
 
 	  /* Decide how much data we can send in one batch.
 	     Long lines need to be split into multiple batches.  */
-	  if (!NILP (XPROCESS (proc)->pty_flag))
+	  if (!NILP (p->pty_flag))
 	    {
 	      /* Starting this at zero is always correct when not the first
                  iteration because the previous iteration ended by sending C-d.
@@ -4933,7 +5259,7 @@
 	  /* Send this batch, using one or more write calls.  */
 	  while (this > 0)
 	    {
-	      int outfd = XINT (XPROCESS (proc)->outfd);
+	      int outfd = XINT (p->outfd);
 	      old_sigpipe = (SIGTYPE (*) ()) signal (SIGPIPE, send_process_trap);
 #ifdef DATAGRAM_SOCKETS
 	      if (DATAGRAM_CHAN_P (outfd))
@@ -4946,7 +5272,18 @@
 		}
 	      else
 #endif
-		rv = emacs_write (outfd, (char *) buf, this);
+		{
+		  rv = emacs_write (outfd, (char *) buf, this);
+#ifdef ADAPTIVE_READ_BUFFERING
+		  if (XINT (p->read_output_delay) > 0
+		      && EQ (p->adaptive_read_buffering, Qt))
+		    {
+		      XSETFASTINT (p->read_output_delay, 0);
+		      process_output_delay_count--;
+		      p->read_output_skip = Qnil;
+		    }
+#endif
+		}
 	      signal (SIGPIPE, old_sigpipe);
 
 	      if (rv < 0)
@@ -4987,8 +5324,7 @@
 		      if (errno == EAGAIN)
 			{
 			  int flags = FWRITE;
-			  ioctl (XINT (XPROCESS (proc)->outfd), TIOCFLUSH,
-				 &flags);
+			  ioctl (XINT (p->outfd), TIOCFLUSH, &flags);
 			}
 #endif /* BROKEN_PTY_READ_AFTER_EAGAIN */
 
@@ -5033,18 +5369,17 @@
     {
 #ifndef VMS
       proc = process_sent_to;
-#endif
-      XPROCESS (proc)->raw_status_low = Qnil;
-      XPROCESS (proc)->raw_status_high = Qnil;
-      XPROCESS (proc)->status = Fcons (Qexit, Fcons (make_number (256), Qnil));
-      XSETINT (XPROCESS (proc)->tick, ++process_tick);
+      p = XPROCESS (proc);
+#endif
+      p->raw_status_low = Qnil;
+      p->raw_status_high = Qnil;
+      p->status = Fcons (Qexit, Fcons (make_number (256), Qnil));
+      XSETINT (p->tick, ++process_tick);
       deactivate_process (proc);
 #ifdef VMS
-      error ("Error writing to process %s; closed it",
-	     SDATA (XPROCESS (proc)->name));
+      error ("Error writing to process %s; closed it", SDATA (p->name));
 #else
-      error ("SIGPIPE raised on process %s; closed it",
-	     SDATA (XPROCESS (proc)->name));
+      error ("SIGPIPE raised on process %s; closed it", SDATA (p->name));
 #endif
     }
 
@@ -5758,7 +6093,10 @@
    queued and the signal-catching function will be continually
    reentered until the queue is empty".  Invoking signal() causes the
    kernel to reexamine the SIGCLD queue.  Fred Fish, UniSoft Systems
-   Inc. */
+   Inc.
+
+   ** Malloc WARNING: This should never call malloc either directly or
+   indirectly; if it does, that is a bug  */
 
 SIGTYPE
 sigchld_handler (signo)
@@ -5876,18 +6214,7 @@
 	  if (WIFEXITED (w))
 	    synch_process_retcode = WRETCODE (w);
 	  else if (WIFSIGNALED (w))
-	    {
-	      int code = WTERMSIG (w);
-	      char *signame;
-
-	      synchronize_system_messages_locale ();
-	      signame = strsignal (code);
-
-	      if (signame == 0)
-		signame = "unknown";
-
-	      synch_process_death = signame;
-	    }
+            synch_process_termsig = WTERMSIG (w);
 
 	  /* Tell wait_reading_process_input that it needs to wake up and
 	     look around.  */
@@ -6281,6 +6608,11 @@
   FD_ZERO (&non_process_wait_mask);
   max_process_desc = 0;
 
+#ifdef ADAPTIVE_READ_BUFFERING
+  process_output_delay_count = 0;
+  process_output_skip = 0;
+#endif
+
   FD_SET (0, &input_wait_mask);
 
   Vprocess_alist = Qnil;
@@ -6298,6 +6630,8 @@
 #ifdef HAVE_SOCKETS
  {
    Lisp_Object subfeatures = Qnil;
+   struct socket_options *sopt;
+
 #define ADD_SUBFEATURE(key, val) \
   subfeatures = Fcons (Fcons (key, Fcons (val, Qnil)), subfeatures)
 
@@ -6316,30 +6650,10 @@
 #if !defined(TERM) && (defined(O_NONBLOCK) || defined(O_NDELAY))
    ADD_SUBFEATURE (QCserver, Qt);
 #endif
-#ifdef SO_BINDTODEVICE
-   ADD_SUBFEATURE (QCoptions, intern ("bindtodevice"));
-#endif
-#ifdef SO_BROADCAST
-   ADD_SUBFEATURE (QCoptions, intern ("broadcast"));
-#endif
-#ifdef SO_DONTROUTE
-   ADD_SUBFEATURE (QCoptions, intern ("dontroute"));
-#endif
-#ifdef SO_KEEPALIVE
-   ADD_SUBFEATURE (QCoptions, intern ("keepalive"));
-#endif
-#ifdef SO_LINGER
-   ADD_SUBFEATURE (QCoptions, intern ("linger"));
-#endif
-#ifdef SO_OOBINLINE
-   ADD_SUBFEATURE (QCoptions, intern ("oobinline"));
-#endif
-#ifdef SO_PRIORITY
-   ADD_SUBFEATURE (QCoptions, intern ("priority"));
-#endif
-#ifdef SO_REUSEADDR
-   ADD_SUBFEATURE (QCoptions, intern ("reuseaddr"));
-#endif
+
+   for (sopt = socket_options; sopt->name; sopt++)
+     subfeatures = Fcons (intern (sopt->name), subfeatures);
+
    Fprovide (intern ("make-network-process"), subfeatures);
  }
 #endif /* HAVE_SOCKETS */
@@ -6432,6 +6746,20 @@
 The value takes effect when `start-process' is called.  */);
   Vprocess_connection_type = Qt;
 
+#ifdef ADAPTIVE_READ_BUFFERING
+  DEFVAR_LISP ("process-adaptive-read-buffering", &Vprocess_adaptive_read_buffering,
+	       doc: /* If non-nil, improve receive buffering by delaying after short reads.
+On some systems, when emacs reads the output from a subprocess, the output data
+is read in very small blocks, potentially resulting in very poor performance.
+This behaviour can be remedied to some extent by setting this variable to a
+non-nil value, as it will automatically delay reading from such processes, to
+allowing them to produce more output before emacs tries to read it.
+If the value is t, the delay is reset after each write to the process; any other
+non-nil value means that the delay is not reset on write.
+The variable takes effect when `start-process' is called.  */);
+  Vprocess_adaptive_read_buffering = Qt;
+#endif
+
   defsubr (&Sprocessp);
   defsubr (&Sget_process);
   defsubr (&Sget_buffer_process);
@@ -6461,10 +6789,18 @@
   defsubr (&Sprocess_list);
   defsubr (&Sstart_process);
 #ifdef HAVE_SOCKETS
-  defsubr (&Sset_network_process_options);
+  defsubr (&Sset_network_process_option);
   defsubr (&Smake_network_process);
   defsubr (&Sformat_network_address);
 #endif /* HAVE_SOCKETS */
+#if defined(HAVE_SOCKETS) && defined(HAVE_NET_IF_H) && defined(HAVE_SYS_IOCTL_H)
+#ifdef SIOCGIFCONF
+  defsubr (&Snetwork_interface_list);
+#endif
+#if defined(SIOCGIFADDR) || defined(SIOCGIFHWADDR) || defined(SIOCGIFFLAGS)
+  defsubr (&Snetwork_interface_info);
+#endif
+#endif /* HAVE_SOCKETS ... */
 #ifdef DATAGRAM_SOCKETS
   defsubr (&Sprocess_datagram_address);
   defsubr (&Sset_process_datagram_address);
@@ -6796,3 +7132,6 @@
 
 
 #endif /* not subprocesses */
+
+/* arch-tag: 3706c011-7b9a-4117-bd4f-59e7f701a4c4
+   (do not change this comment) */