#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>

#ifndef INADDR_NONE
#define INADDR_NONE -1
#endif

#define BUFSIZE 32

struct termios temp_tios;
int temp;
temp = 0;

int CleanUp( i )
int i;
{
	if( temp )
		tcsetattr( 0, TCSANOW, &temp_tios );
	fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) & ~O_NDELAY );
	exit( 0 );
}

int HandleIO( fd )   /* Shuffle data between fd and stdin/stdout */
int fd;
{
	int result, i;
	char inbuf[BUFSIZE];
	struct fd_set rd;
	struct fd_set zer;
	struct termios tios;
	struct timeval timeout;

	signal( SIGINT,  (void *) CleanUp );	/* Clean up sockets when killed */
	signal( SIGTSTP, (void *) CleanUp );
	signal( SIGSEGV, (void *) CleanUp );
	signal( SIGBUS,  (void *) CleanUp );
	signal( SIGQUIT, (void *) CleanUp );
	signal( SIGKILL, (void *) CleanUp );

	tcgetattr( 0, &tios );		/* Get attributes for stdin				*/
	temp_tios = tios;			/* Make copy of tios for restoration	*/
	temp =1;
	tios.c_lflag &= ~ICANON;	/* Disable canonical input              */
	tios.c_cc[VMIN] = 1;		/* Read at least 1 character			*/
	tios.c_cc[VTIME] = 0;		/* Disable timeout						*/
	tios.c_lflag &= ~ECHO;		/* No echo								*/
	tios.c_lflag &= ~ISIG;		/* Disable input signals				*/
	tios.c_cflag &= ~CSIZE;
	tios.c_cflag |=  CS8;		/* 8 bit connection						*/
	tios.c_cflag &= ~PARENB;	/* No output parity						*/
	tios.c_iflag &= ~INPCK;		/* No input parity						*/
	tios.c_iflag &= ~ISTRIP;	/* Do not strip 8th bit					*/
	tios.c_iflag &= ~IXON;		/* Disable XON/XOFF						*/
	tios.c_iflag &= ~IXOFF;
	tios.c_iflag &= ~INLCR;		/* No NL -> CR remapping				*/
	tios.c_iflag &= ~ICRNL;		/* No NL -> CR remapping				*/
	tios.c_oflag &= ~OPOST;		/* Disable output post processing		*/

	tcsetattr( 0, TCSANOW, &tios );

	timeout.tv_sec = 1;
	timeout.tv_usec = 0;

	fcntl( 0, F_SETFL, O_NDELAY );	/* Disable read blocking */
	fcntl( fd, F_SETFL, O_NDELAY );
	while( 1 )
	{
		FD_ZERO( &rd );
		FD_SET( 0, &rd );	/* Determine whether 0 can be read	*/
		FD_SET( fd, &rd );	/* Determine whether fd can be read	*/
		FD_ZERO( &zer );	/* No write or exception queries	*/

		if( select( NFDBITS, &rd, &zer, &zer, &timeout ) != -1 )
		{
			if( FD_ISSET( 0, &rd ) )
			{  /* read zero */
				result = read( 0, inbuf, BUFSIZE );
				if( result > 0 )
				{
					write( fd, inbuf, result );
				}
				if( result < 0 )
				{
					printf( "\n\nEOF detected, exiting.\n" );
					CleanUp( 0 );
				}
			}
			if( FD_ISSET( fd, &rd ) )
			{
				result = read( fd, inbuf, BUFSIZE );
				if( result > 0 )
					write( 1, inbuf, result );
				if( result < 0 )
				{
					printf( "\n\nERROR: Connection to server lost, quitting.\n" );
					CleanUp(0);
				}
			}
		}
	}
}

int ConnectToServer( servername, port )
char *servername;
int port;
{
	int sock;
	struct hostent *host;
	struct sockaddr_in serv;

	printf( "Connecting to server %s port %ld.\n", servername, port );

	sock = socket( AF_INET,SOCK_STREAM,0 );
	if( sock < 0 )
	{
		fprintf( stderr, "irclink: Could not get socket.\n" );
		return( -1 );
	}

	serv.sin_addr.s_addr = inet_addr( servername );
	serv.sin_port = htons( port );
	serv.sin_family = AF_INET;

	if( serv.sin_addr.s_addr == INADDR_NONE )
	{
		if( host = (struct hostent *) gethostbyname( servername ) )
		{
			memcpy( &serv.sin_addr, host->h_addr, host->h_length );
		}
		else
		{
			fprintf( stderr, "irclink: Unknown host.\n" );
			return( -1 );
		}
	}

	if( connect( sock, &serv, sizeof( struct sockaddr_in ) ) == -1)
		return( -1 );
	else
		return( sock );
}

main( argc, argv )
int argc;
char **argv;
{
	char buf[2];
	char *cmdarg;
	char *servername;
	int sock;
	int port;
	port = 6667;

	if( argv[1] )
		servername = argv[1];
	else
	{
		printf( "Usage 2: %s server [port]\n", argv[0] );
		exit( 1 );
	}

	if( argv[2] )
		port = atoi( argv[2] );

	sock = ConnectToServer( servername, port );

	if( sock < 0 )
	{
		fprintf( stderr, "irclink: Could not connect to server.\n" );
		exit( 5 );
	}

	printf( "Connected to server %s.\n", servername );
	HandleIO( sock );
	CleanUp( 0 );    /* make sure to clean up */
	return( 0 );
}
