/*
 * newio.c: This is some handy stuff to deal with file descriptors in a way
 * much like stdio's FILE pointers 
 *
 * IMPORTANT NOTE:  If you use the routines here-in, you shouldn't switch to
 * using normal reads() on the descriptors cause that will cause bad things
 * to happen.  If using any of these routines, use them all 
 *
 * Written By Michael Sandrof
 *
 * Copyright(c) 1990 
 *
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 */
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#ifdef ISC22
# include <sys/bsdtypes.h>
#endif /* ISC22 */
#ifdef ESIX
# include <lan/net_types.h>
#endif /* ESIX */
#ifdef SVR4
# define bcopy(a,b,c) memmove(b,a,c)
#endif /* SVR4 */
#ifdef POSIX
# include <sys/select.h>
#endif /* POSIX */
#include <ctype.h>

#ifdef _IBMR2
# include <sys/select.h>
#endif

/* stuff that exists on 4.3 machines, so I stuck it in here */
#ifndef NBBY
#define	NBBY	8		/* number of bits in a byte */
#endif /* NBBY */

#ifndef NFDBITS
#define NFDBITS	(sizeof(long) * NBBY)	/* bits per mask */
#endif /* NFDBITS */

#ifndef FD_SET
#define	FD_SET(n, p)	((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
#endif /* FD_SET */

#ifndef FD_CLR
#define	FD_CLR(n, p)	((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
#endif /* FD_CLR */

#ifndef FD_ISSET
#define	FD_ISSET(n, p)	((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
#endif /* FD_ISSET */

#ifndef FD_ZERO
#define FD_ZERO(p)	bzero((char *)(p), sizeof(*(p)))
#endif /* FD_ZERO */

#ifndef	FD_SETSIZE
#define	FD_SETSIZE	256
#endif /* FD_SETSIZE */

#define null(foo) (foo) 0
#define IO_BUFFER_SIZE 512

#define	WAIT_NL ((unsigned) 0x0001)

typedef	struct	myio_struct
{
	char	buffer[IO_BUFFER_SIZE + 1];
	unsigned int	read_pos,
			write_pos;
	unsigned	misc_flags;
#ifdef ESIX
	unsigned	flags;
#endif /* ESIX */
}           MyIO;

#define IO_SOCKET 1

static	struct	timeval	right_away = { 0L, 0L };
static	MyIO	*io_rec[NFDBITS];

static	struct	timeval	dgets_timer;
static	struct	timeval	*timer;

#ifdef ESIX
/* Esix must know if it is a socket or not. */
void	mark_socket(int des)
{

	if (first)
	{
		int	c;
		for (c = 0; c < FD_SETSIZE; c++)
			io_rec[c] = null(MyIO *);
		first = 0;
	}
	if (io_rec[des] == null(MyIO *))
	{
		io_rec[des] = (MyIO *) new_malloc(sizeof(MyIO));
		io_rec[des]->read_pos = 0;
		io_rec[des]->write_pos = 0;
		io_rec[des]->flags = 0;
	}
	io_rec[des]->flags |= IO_SOCKET;
}

void	unmark_socket(int des)
{

	if (first)
	{
		int	c;
		for (c = 0; c < FD_SETSIZE; c++)
			io_rec[c] = null(MyIO *);
		first = 0;
	}
	if (io_rec[des] == null(MyIO *))
	{
		io_rec[des] = (MyIO *) new_malloc(sizeof(MyIO));
		io_rec[des]->read_pos = 0;
		io_rec[des]->write_pos = 0;
		io_rec[des]->flags = 0;
	}
	io_rec[des]->flags &= ~IO_SOCKET;
}
#endif /* ESIX */

/*
 * dgets_timeout: does what you'd expect.  Sets a timeout in seconds for
 * dgets to read a line.  
 */
void	dgets_timeout(sec)
long	sec;
{
	if (sec)
	{
		dgets_timer.tv_sec = sec;
		dgets_timer.tv_usec = 0;
		timer = &dgets_timer;
	}
	else
		timer = null(struct timeval *);
}

static	void	init_io()
{
	static	int	first = 1;

	if (first)
	{
		int	c;

		for (c = 0; c < NFDBITS; c++)
			io_rec[c] = null(MyIO *);
		dgets_timeout(0L);
		first = 0;
	}
}

/*
 * dgets: works much like fgets except on descriptor rather than file
 * pointers.  Returns the number of character read in.  Returns 0 on EOF and
 * -1 on a timeout (see dgets_timeout()) 
 */
int	dgets(str, len, des, specials)
char	*str;
int	len;
int	des;
char	*specials;
{
	char	*ptr, ch;
	int	cnt = 0,
		c;
	fd_set	rd;
	int	WantNewLine = 0;
	int	BufferEmpty;
	int	i,
		j;

	init_io();
	if (io_rec[des] == null(MyIO *))
	{
		io_rec[des] = (MyIO *) new_malloc(sizeof(MyIO));
		io_rec[des]->read_pos = 0;
		io_rec[des]->write_pos = 0;
		io_rec[des]->misc_flags = 0;
#ifdef ESIX
		io_rec[des]->flags = 0;
#endif /* ESIX */	
	}
	if (len < 0)
	{
		WantNewLine = 1;
		len = (-len);
		io_rec[des]->misc_flags |= WAIT_NL;
	}
	while (1)
	{
		if ((BufferEmpty = (io_rec[des]->read_pos ==
				io_rec[des]->write_pos)) || WantNewLine)
		{
			if(BufferEmpty)
			{
				io_rec[des]->read_pos = 0;
				io_rec[des]->write_pos = 0;
			}
			FD_ZERO(&rd);
			FD_SET(des, &rd);
			switch (select(NFDBITS, &rd, 0, 0, timer))
			{
			case 0:
				str[cnt] = null(char);
				return (-1);
			default:
#ifdef ESIX
				if (io_rec[des]->flags & IO_SOCKET)
					c = recv(des, io_rec[des]->buffer +
					  io_rec[des]->write_pos,
					  IO_BUFFER_SIZE-io_rec[des]->write_pos,
					  0);
				else
#endif /* ESIX */
					c = read(des, io_rec[des]->buffer +
					 io_rec[des]->write_pos,
					 IO_BUFFER_SIZE-io_rec[des]->write_pos);
				if (c <= 0)
					return(0);
				if (WantNewLine && specials)
				{
					ptr = io_rec[des]->buffer;
					for (i = io_rec[des]->write_pos;
					    i < io_rec[des]->write_pos+c;i++)
	/* This section re-indented - phone, jan 1993 */
			{
				if((ch = ptr[i]) == specials[0])
				{
					if (i > 0)
					{
						bcopy(ptr + i - 1, ptr + i + 1,
						    io_rec[des]->write_pos +
						    c - i - 1);
						i -= 2;
						c -= 2;
					}
					else
					{
						bcopy(ptr, ptr + 1,
						    io_rec[des]->write_pos +
						    c - 1);
						i--;
						c--;
					}
				}
				else if (ch == specials[2])
				{
					for (j = i - 1; j >= 0 &&
							isspace(ptr[j]); j--)
						;
					for (;j >= 0 && !isspace(ptr[j]); j--)
						;
					bcopy(ptr + j + 1, ptr + i + 1,
					    io_rec[des]->write_pos + c - i - 1);
					c -= (i - j);
					i = j;
				}
				else if (ch == specials[1])
				{
					for (j = i - 1;
						j >= 0 && ptr[j] != '\n'; j--);
					bcopy(ptr + j + 1, ptr + i + 1,
					    io_rec[des]->write_pos + c - i - 1);
					c -= (i-j);
					i = j;
				}
			}
				}
				io_rec[des]->write_pos += c;
				break;
			}
		}
		ptr = io_rec[des]->buffer;
		if (WantNewLine)
		{
			for (cnt = io_rec[des]->write_pos; cnt > 0;cnt--, ptr++)
			{
				if (*ptr == '\n' || cnt == len-1)
				{
					*ptr = '\0';
					strcpy(str, io_rec[des]->buffer);
					io_rec[des]->write_pos=cnt-1;
					bcopy(io_rec[des]->buffer, ptr, cnt);
					return 1;
				}
			}
			return -2;
		}
		while (io_rec[des]->read_pos < io_rec[des]->write_pos)
		{
			if (((str[cnt++] = ptr[(io_rec[des]->read_pos)++])
				== '\n') || (cnt == len))
			{
				str[cnt] = null(char);
				return (cnt);
			}
		}
	}
}

/*
 * new_select: works just like select(), execpt I trimmed out the excess
 * parameters I didn't need.  
 */
int	new_select(rd, wd, timeout)
fd_set	*rd,
	*wd;
struct	timeval	*timeout;
{
	int	i,
		set = 0;
		fd_set new;
	struct	timeval	newtimeout;

	bcopy(timeout, &newtimeout, sizeof(struct timeval));
	init_io();
	FD_ZERO(&new);
	for (i = 0; i < NFDBITS; i++)
	{
		if (io_rec[i] && !(io_rec[i]->misc_flags&WAIT_NL))
		{
			if (io_rec[i]->read_pos < io_rec[i]->write_pos)
			{
				FD_SET(i, &new);
				set = 1;
			}
		}
	}
	if (set)
	{
		set = 0;
		if (!(select(NFDBITS, rd, wd, NULL, &right_away) > 0))
			FD_ZERO(rd);
		for (i = 0; i < NFDBITS; i++)
		{
			if ((FD_ISSET(i, rd)) || (FD_ISSET(i, &new)))
			{
				set++;
				FD_SET(i, rd);
			}
			else
				FD_CLR(i, rd);
		}
		return (set);
	}
	return (select(NFDBITS, rd, wd, NULL, &newtimeout));
}

/* new_close: works just like close */
void	new_close(des)
int	des;
{
#ifdef ESIX
	if (io_rec[des]->flags & IO_SOCKET)
		t_close(des);
#endif /* ESIX */
	new_free(&(io_rec[des]));
	close(des);
}
