
/* open a TCP network connection to a given HOST/SERVICE.  Treated
   exactly like a normal process when reading and writing.  Only
   differences are in status display and process deletion.  A network
   connection has no PID; you cannot signal it.  All you can do is
   deactivate and close it via delete-process */

#ifdef HAVE_UNIX_DOMAIN
#include <sys/un.h>
#endif HAVE_UNIX_DOMAIN

DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, 
       4, 4, 0,
#ifdef HAVE_UNIX_DOMAIN
  "Open a TCP connection for a service to a host.\n\
Returns a subprocess-object to represent the connection.\n\
Input and output work as for subprocesses; `delete-process' closes it.\n\
Args are NAME BUFFER HOST SERVICE.\n\
If SERVICE is 0, then HOST is taken to be the name of a socket file, and a\n\
Unix domain socket is opened.\n\
NAME is name for process.  It is modified if necessary to make it unique.\n\
BUFFER is the buffer (or buffer-name) to associate with the process.\n\
 Process output goes at end of that buffer, unless you specify\n\
 an output stream or filter function to handle the output.\n\
 BUFFER may be also nil, meaning that this process is not associated\n\
 with any buffer\n\
Third arg is name of the host to connect to.\n\
Fourth arg SERVICE is name of the service desired, or an integer\n\
 specifying a port number to connect to."
#else
  "Open a TCP connection for a service to a host.\n\
Returns a subprocess-object to represent the connection.\n\
Input and output work as for subprocesses; `delete-process' closes it.\n\
Args are NAME BUFFER HOST SERVICE.\n\
NAME is name for process.  It is modified if necessary to make it unique.\n\
BUFFER is the buffer (or buffer-name) to associate with the process.\n\
 Process output goes at end of that buffer, unless you specify\n\
 an output stream or filter function to handle the output.\n\
 BUFFER may be also nil, meaning that this process is not associated\n\
 with any buffer\n\
Third arg is name of the host to connect to.\n\
Fourth arg SERVICE is name of the service desired, or an integer\n\
 specifying a port number to connect to."
#endif HAVE_UNIX_DOMAIN
       )
   (name, buffer, host, service)
      Lisp_Object name, buffer, host, service;
{
  Lisp_Object proc;
  register int i;
  struct sockaddr_in address;
#ifdef HAVE_UNIX_DOMAIN
  struct sockaddr_un server;
  int unix_domain = 0;
#endif HAVE_UNIX_DOMAIN
  struct servent *svc_info;
  struct hostent *host_info;
  int s, outch, inch;
  char errstring[80];
  int port;

  CHECK_STRING (name, 0);
  CHECK_STRING (host, 0);
  if (XTYPE(service) == Lisp_Int)
    {
#ifdef HAVE_UNIX_DOMAIN
	if (XINT (service) == 0)
	  unix_domain = 1;
	else
#endif HAVE_UNIX_DOMAIN
	  port = htons ((unsigned short) XINT (service));
    }
  else
    {
      CHECK_STRING (service, 0);
      svc_info = getservbyname (XSTRING (service)->data, "tcp");
      if (svc_info == 0)
	error ("Unknown service \"%s\"", XSTRING (service)->data);
      port = svc_info->s_port;
    }

#ifdef HAVE_UNIX_DOMAIN
  if (unix_domain)
    {
	server.sun_family = AF_UNIX;
	strcpy (server.sun_path, XSTRING (host)->data);
    }
  else
#endif HAVE_UNIX_DOMAIN
    {
	host_info = gethostbyname (XSTRING (host)->data);
	if (host_info == 0)
	  error ("Unknown host \"%s\"", XSTRING(host)->data);

	bzero (&address, sizeof address);
	bcopy (host_info->h_addr, (char *) &address.sin_addr, host_info->h_length);
	address.sin_family = host_info->h_addrtype;
	address.sin_port = port;
    }

#ifdef HAVE_UNIX_DOMAIN
  if (unix_domain)
    s = socket (AF_UNIX, SOCK_STREAM, 0);
  else
#endif HAVE_UNIX_DOMAIN
    s = socket (host_info->h_addrtype, SOCK_STREAM, 0);
  
  if (s < 0)
    report_file_error ("error creating socket", Fcons (name, Qnil));

#ifdef HAVE_UNIX_DOMAIN
  if (unix_domain)
    {
	if (connect (s, &server, strlen (server.sun_path) + 2) < 0)
	  error ("connect failed for socket: \"%s\"", XSTRING (host)->data);
    }
  else
#endif HAVE_UNIX_DOMAIN
    {
	if (connect (s, &address, sizeof address) == -1)
	  error ("Host \"%s\" not responding", XSTRING (host)->data);
    }

  inch = s;
  outch = dup (s);
  if (outch < 0) 
    report_file_error ("error duplicating socket", Fcons (name, Qnil));

  if (!NULL (buffer))
    buffer = Fget_buffer_create (buffer);
  proc = make_process (name);

  chan_process[inch] = proc;

#ifdef O_NDELAY
  fcntl (inch, F_SETFL, O_NDELAY);
#endif

  XPROCESS (proc)->childp = host;
  XPROCESS (proc)->command_channel_p = Qnil;
  XPROCESS (proc)->buffer = buffer;
  XPROCESS (proc)->sentinel = Qnil;
  XPROCESS (proc)->filter = Qnil;
  XPROCESS (proc)->command = Qnil;
  XPROCESS (proc)->pid = Qnil;
  XPROCESS (proc)->kill_without_query = Qt;
  XFASTINT (XPROCESS (proc)->infd) = s;
  XFASTINT (XPROCESS (proc)->outfd) = outch;
  XFASTINT (XPROCESS (proc)->flags) = RUNNING;
  input_wait_mask |= ChannelMask (inch);
  return proc;
}


DEFUN ("accept-process-output", Faccept_process_output, Saccept_process_output,
  0, 2, 0,
  "Allow any pending output from subprocesses to be read by Emacs.\n\
It is read into the process' buffers or given to their filter functions.\n\
Non-nil arg PROCESS means do not return until some output has been received\n\
from PROCESS.  Non-nil arg TIMEOUT means wait for that many seconds, -1\n\
return immediately.")
  (proc, timeout)
     register Lisp_Object proc, timeout;
{
  if (NULL (proc)) {
      if (XTYPE(timeout) == Lisp_Int)
        timeout = XINT(timeout);
      else
        timeout = -1;
      wait_reading_process_input (timeout, 0, 0);
    }
  else
    {
      if (XTYPE(timeout) == Lisp_Int)
        timeout = XINT(timeout);
      else
        timeout = 0;
      proc = get_process (proc);
      wait_reading_process_input (timeout, XPROCESS (proc), 0);
    }
  return Qnil;
}
