#define I_SYS
#define I_SOCKET
#define I_ERRNO
#define I_STRING

#include "includes.h"
#include <arpa/inet.h>

#if defined(NO_UNIX_DOMAIN) || defined(TERM_NFS_DIR)
#include <fcntl.h>
#endif

int bind_tcp(int port)
{
  struct sockaddr_in sin;
  int s;

/* #if 0 */
/* This is not necessary here! - ot */
/* If we don't put this here, we need it with getsockname. bcr */
  struct hostent *hp;
  char host_name[258];

#if defined(SYSV) && !defined(DYNIXPTX) 
  struct utsname unam;

  if (uname(&unam) == -1) {
    perror("Term: uname");
    return -1;
  }
  strcpy(host_name, unam.nodename);
#else
  if (gethostname(host_name, sizeof(host_name)) == -1) {
    perror("Term: gethostname");
    return -1;
    }  
#endif /* SYSV */

  hp = gethostbyname(host_name); /* Find the name of the machine we */
				/* are running.. */
  if (hp == 0) {		/* Probly doesn't run TCP/IP. oh well. */
    fprintf(stderr, "Gethostbyname: Unknown host.\n");
    return -1;
    }

  memset(&sin, 0, sizeof(sin));
  memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
  sin.sin_family = hp->h_addrtype;
/* #endif  */
  sin.sin_port = htons( port );
/*  sin.sin_addr.s_addr = INADDR_ANY; */
  s = socket(hp->h_addrtype, SOCK_STREAM, 0); 
/*  s = socket(AF_INET, SOCK_STREAM, 0); */

  if (s == -1) {
    perror("Term: socket");
    return -1;
    }
				/* Ok. Now we look for a socket that */
				/* isn't bound. This is probably a */
				/* little scummy, but some ppl like it */
				/* this way.. We try a maximum of 100 */
				/* times... */
  while (bind(s, (struct sockaddr * ) &sin,
	      sizeof(sin)) < 0) { /* while an error.. */
    if (errno != EADDRINUSE) {	/* if it wasn't in use */
      close(s);
      return -1; 		/* then we can't handle it, so abort. */
    }
    close(s);
    return -2;			/* handled specially by some clients. */
  } /*NOTREACHED*/

  if (listen(s, 5) == -1) {	/* If we can't listen... */
    perror("Term: listen");	/* then just dump. We can't handle */
				/* errors here. */
    close(s);
    return -1;
    }
  return s;
}


/*      if NO_UNIX_DOMAIN
 *
 *	We bind the socket, and then write the address to the requested
 *	file.  We are the server.
 */

/*	if STREAMS_PIPE
 *
 *	This is a bit of a hack.  If STREAMS_PIPE is defined, we look for the
 *	environment variable TERM_BORROWED_DISPLAY_NUMBER, and interpret the
 *	value as an X-server display number, and use that streams pipe as
 *	our client/server IPC channel.
 *
 *	This is for SCO.  It would probably work on other systems if you
 *	became root and set up the appropriate /dev/X?R and /dev/X?S entries
 */

int bind_unix(char *path) 
{
#if defined(NO_UNIX_DOMAIN) || defined(TERM_NFS_DIR)
  struct sockaddr_in sock_in;
  int size_in;
  int fd;
#else
  struct sockaddr_un sock_un;
#endif
#ifdef STREAMS_PIPE
  char *borrowed_display_number;
#endif

  int s;

#ifdef STREAMS_PIPE
  borrowed_display_number = getenv("TERM_BORROWED_DISPLAY_NUMBER");
  if (borrowed_display_number != 0) {
	s = open_stream_pipe(atoi(borrowed_display_number));
	if (s < 0) {
		fprintf(stderr, "cannot borrow X display channel %s\n", borrowed_display_number);
		return -1;
	} else {
		fprintf(stderr, "Borrowed X display channel %d\n", atoi(borrowed_display_number));
		return s;
	}
  }

#endif

#if defined(NO_UNIX_DOMAIN) || defined(TERM_NFS_DIR)
  if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
    perror("Term: Socket");
    return -1;
  }

  sock_in.sin_family = AF_INET;
  sock_in.sin_addr.s_addr = htonl(INADDR_ANY);
  sock_in.sin_port = htons(0);
  if (bind(s, (struct sockaddr *) &sock_in, sizeof(sock_in)) < 0) {
    perror("Term: Bind");
    close(s);
    return -1;
  }

  size_in = sizeof(sock_in);
  if (getsockname(s, (struct sockaddr *) &sock_in, &size_in) < 0) {
	perror("Term: socket: getsockname");
	close(s);
	return -1;
  }
  fd = open(path, O_WRONLY|O_CREAT, 0666);
  if (fd < 0) {
	perror("Term: open path");
	close(s);
	return -1;
  }

  if (write(fd, &sock_in.sin_port, sizeof(sock_in.sin_port)) < 0) {
	perror("Term: write port number");
	close(fd);
	close(s);
	return -1;
  }

  fprintf(stderr, "Port is %d\n", sock_in.sin_port);
#else
  if ((s = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0) {
    perror("Term: Socket");
    return -1;
  }

  sock_un.sun_family = AF_UNIX;
  
  strcpy(sock_un.sun_path, path);
  unlink(sock_un.sun_path);
  if (bind(s, (struct sockaddr *) &sock_un, strlen(sock_un.sun_path) + 2)) {
    perror("Term: Bind");
    fprintf(stderr,"Can't bind a socket at %s\n",path);
    close(s);
    return -1;
  }

#endif
  /* ok. Start looking for connections. */
  if (listen(s, 5) < 0) {
    perror("Term: Listen");
    close(s);
    return -1;
  }
  return s;
}

/* if STREAMS_PIPE
 *
 *	This is a bit of a hack.  If STREAMS_PIPE is defined, we look for the
 *	environment variable TERM_BORROWED_DISPLAY_NUMBER, and interpret the
 *	value as an X-server display number, and use that streams pipe as
 *	our client/server IPC channel.
 *
 *	This is for SCO.  It would probably work on other systems if you
 *	became root and set up the appropriate /dev/X?R and /dev/X?S entries
 */


int open_unix(char *p){
  return connect_unix(-1,p);
};

/*      To hack things even more, I now have tried to make it possable to
 *      connect non-unix type sockets to term with the command C_BINDS. (bcr)
 */

int connect_unix(int S, char *p)
{
  struct sockaddr_in sock_in;
#if !defined(NO_UNIX_DOMAIN) && !defined(TERM_NFS_DIR)
  struct sockaddr_un sock_un;
#endif
#ifdef STREAMS_PIPE
  char *borrowed_display_number;
#endif
  int s = -1;

#ifdef STREAMS_PIPE
  borrowed_display_number = getenv("TERM_BORROWED_DISPLAY_NUMBER");
  if (borrowed_display_number != 0) {
	if(S<0)
          s = MakeStreamPipeConnection(atoi(borrowed_display_number));
        else
          s = S;
	if (s < 0) {
		fprintf(stderr, "cannot borrow X display channel %s\n", borrowed_display_number);
		return -1;
	} else {
		return s;
	}
  }

#endif

#if !defined(NO_UNIX_DOMAIN) && !defined(TERM_NFS_DIR)
  if(S>=0){
    int size_in;

    size_in = sizeof(sock_in);
    if (getsockname(S, (struct sockaddr *) &sock_in, &size_in) < 0) {
  	perror("Term: socket: getsockname");
	close(s);
	return -1;
    }
    if(sock_in.sin_family==AF_UNIX) s=S;
  };
  if (s<0) if ((s = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0) {
    perror("Term: Socket");
    return -1;
  };

  sock_un.sun_family =  AF_UNIX; 
  
  sprintf(sock_un.sun_path,"%s",p);
  
  if (connect(s, (struct sockaddr *) &sock_un, 
	      strlen(sock_un.sun_path) + 2)) {
    if(term_debug>=0) perror("Term: Connect");
    close(s);
    return -1;
  }
  if(S>=0 && S!=s){
    char port[10];

    if(send_command(s,C_BINDS,1,"%d",0)<0){
      perror("Term: C_BINDS");
      close(s);
      return -1;
    };
    if(read(s,port,sizeof(port))<0){
      perror("Term: read port");
      close(s);
      return -1;
    };
    if(port[2]!= I_OK){
      perror("Term read port");
      close(s);
      return -1;
    };
    sock_in.sin_port=htons(atoi(&port[3]));
    close(s);
#else /* NO_UNIX_DOMAIN */
  {
    int fd;

    fd = open(p, O_RDONLY);
    if (fd < 0) {
	perror("Term: open port path");
	return -1;
    }

    if (read(fd, &sock_in.sin_port, sizeof(sock_in.sin_port)) < 0) {
	perror("Term: read port path");
	close(fd);
	return -1;
    }

    close(fd);
#endif
    if(S>=0){
      s=S;
    }else if ((s = socket(AF_INET, SOCK_STREAM, 0 )) < 0) {
      perror("Term: Socket");
      return -1;
    }

    sock_in.sin_family = AF_INET;
    sock_in.sin_addr.s_addr = inet_addr("127.0.0.1");

    if (connect(s, (struct sockaddr *) &sock_in, sizeof(sock_in))) {
      if(term_debug>=0) perror("Term: Connect");
      close(s);
      return -1;
    };
  };
  return s;
  
}


