/
help/
log/
player/
post/
rooms/
util/
util/italk/
util/list/
util/msg/
util/muddle/
/*
 * IO.C:
 *
 *	Socket input/output/establishment functions.
 *
 *	Copyright (C) 1991, 1992, 1993 Brett J. Vickers
 *
 */

#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include "mstruct.h"
#include "mextern.h"

#define buflen(a,b,c)	(a-b + (a<b ? c:0))
#define saddr		addr.sin_addr.s_addr
#define MAXPLAYERS	80

typedef struct wq_tag {
	int		fd;
	struct wq_tag	*next_tag;
} wq_tag;

int				Numplayers;
int				Numwaiting;
int				Deadchildren;
static wq_tag			*First_wait;
static int			Waitsock;
static fd_set			Sockets;
extern int			Port;

/**********************************************************************/
/*				sock_init			      */
/**********************************************************************/

/* This function initializes the socket that is used to accept new  */
/* connections on.						    */

void sock_init(port, debug)
int	port;
int	debug;
{
	struct sockaddr_in 	addr;
	struct linger		ling;
	int 			n, i;
	extern char		report;

	if(debug) {
		FD_SET(0, &Sockets);
		FD_SET(1, &Sockets);
		FD_SET(2, &Sockets);
	}
	signal(SIGPIPE, SIG_IGN);
	signal(SIGTERM, SIG_IGN);
	signal(SIGCHLD, child_died);

	Tablesize = getdtablesize();
	if (Tablesize > PMAX){
		Tablesize = PMAX;
		logf("Tablesize greater than PMAX\n");
	}
	Waitsock = socket(AF_INET, SOCK_STREAM, 0);
	if(Waitsock < 0)
		exit(-1);
	FD_SET(Waitsock, &Sockets);

	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);

	if (report){
   	i = 1;
        setsockopt(Waitsock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int));
	}
       
	n = bind(Waitsock, (struct sockaddr *) &addr, sizeof(addr));
	if(n < 0)
		exit(-1);

	ling.l_onoff = ling.l_linger = 0;
	setsockopt(Waitsock, SOL_SOCKET, SO_LINGER, (char *)&ling,
		   sizeof(struct linger));

	listen(Waitsock, 5);

	i=1;
	ioctl(Waitsock, FIONBIO, &i);
}

/**********************************************************************/
/*				sock_loop			      */
/**********************************************************************/

/* This function is the main loop of the entire program.  It constantly */
/* checks for new input, outputs players' output buffers, handles the   */
/* players' commands and updates the game.				*/

void sock_loop()
{
	while(1) {
		if(Deadchildren) reap_children();
		io_check();
		output_buf();
		handle_commands();
		update_game();
	}
}

/**********************************************************************/
/*				io_check			      */
/**********************************************************************/

/* This function takes a look at all the sockets that are being used at */
/* the time, and determines which ones have input waiting on them.  The */
/* ones that do call accept_input to have their input buffers updated.  */
/* If the wait socket indicates input is ready to read, that means a    */
/* new connection to the game must be accepted. 			*/

int io_check()
{
	fd_set			sockcheck;
	short			rtn=0, i;
	static struct timeval	t = {0L, 75000L};

	sockcheck = Sockets;
/*	t.tv_usec = T_USEC; */
/*	t.tv_sec = 0;	    */
	t.tv_usec = 75000L;
	if(select(Tablesize, &sockcheck, 0, 0, &t) > 0) {
		for(i=0; i<Tablesize; i++) {
			if(FD_ISSET(i, &sockcheck)) {
				if(i != Waitsock)
					rtn |= accept_input(i);
				else
					accept_connect();
			}
		}
	}

	return(rtn);
}

/**********************************************************************/
/*				accept_connect			      */
/**********************************************************************/

/* This function accepts a new connection on the wait socket so that a  */
/* new player can begin playing.  The player's iobuf structure is init- */
/* ialized and a spot is marked in the player and socket arrays for the */
/* player.								*/

void accept_connect()
{
	int			len, fd, i=1, pid;
	iobuf			*io;
	extra			*extr;
	struct linger		ling;
	struct sockaddr_in	addr;
	char			*inetname(), path[127], 
				port1str[10], port2str[10];

	addr.sin_family = AF_INET;
	addr.sin_port = htons(Port);
	addr.sin_addr.s_addr = INADDR_ANY;

	len = sizeof(struct sockaddr_in);
	fd = accept(Waitsock, (struct sockaddr *) &addr, &len);
	if (fd < 0)
		merror("accept_connect", FATAL);

	ioctl(fd, FIONBIO, &i);

	ling.l_onoff = ling.l_linger = 0;
	setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&ling,
		   sizeof(struct linger));

	io = (iobuf *)malloc(sizeof(iobuf));
	extr = (extra *)malloc(sizeof(extra));
	if(!io || !extr) 
		merror("accept_connect", FATAL);

	Ply[fd].io = io;
	Ply[fd].ply = 0;
	Ply[fd].extr = extr;

	zero(extr, sizeof(extra));
	zero(io, sizeof(iobuf));
	io->ltime = time(0);
	io->intrpt = 1;

	strcpy(io->address, inetname(addr.sin_addr));

	pid = vfork();
	if(!pid) {
		sprintf(path, "%s/auth", BINPATH);
		sprintf(port1str, "%d", ntohs(addr.sin_port));
		sprintf(port2str, "%d", Port);
		execl(path, "auth", io->address, port1str, port2str, 0);
		exit(0);
	}
	else {
		strcpy(io->userid, "unknown");
		io->lookup_pid = pid;
	}

	FD_SET(fd, &Sockets);

	if(Numplayers > Tablesize-2) {
		print(fd, "Game full.  Try again later.\n");
		disconnect(fd);
		return;
	}

	else if(Numplayers >= MAXPLAYERS &&
		((unsigned)(ntohl(saddr))>>24) != 127) {
		if(Numwaiting > MAXPLAYERS) {
			write(fd, "Queue full.\n\r", 13);
			disconnect(fd);
			return;
		}
		add_wait(fd);
		RETURN(fd, waiting, 1);
	}

	init_connect(fd);
}

/************************************************************************/
/*				init_connect				*/
/************************************************************************/

/* This function sets up the player using the fd'th input socket.	*/

void init_connect(fd)
int	fd;
{
	int		i;

	/*************************************************************/
	/* The following lines must be left intact as part of the    */
	/* copyright agreement.					     */
	/*************************************************************/
	print(fd, "\nMordor v2.5 Beta (C) 1992, 1995");
	print(fd, "\nProgrammed by Brett J. Vickers");
	print(fd, "\nEnhancements by Steve Smith and Brooke Paul");
	/*************************************************************/
	/*************************************************************/

	Ply[fd].io->intrpt |= 2;
	Numplayers++;

	if((i = locked_out(fd)) > 1) {
		print(fd, "\nA password is required to play from that site.");
		print(fd, "\nPlease enter site password: ");
		output_buf();
		RETURN(fd, login, 0);
	}
	else if(i) {
		disconnect(fd);
		return;
	}

	Ply[fd].io->ltime = time(0);
	print(fd, "\n\nPlease enter name: ");
	output_buf();
	RETURN(fd, login, 1);

}

/************************************************************************/
/*				locked_out				*/
/************************************************************************/

/* This function determines if the player on socket number, fd, is on	*/
/* a site that is being locked out.  If the site is password locked,	*/
/* then 2 is returned.  If it's completely locked, 1 is returned.  If	*/
/* it's not locked out at all, 0 is returned.				*/

int locked_out(fd)
int 	fd;
{
	int	i;

	for(i=0; i<Numlockedout; i++) {
		if(!addr_equal(Lockout[i].address, Ply[fd].io->address))
			continue;

		if(Lockout[i].password[0]) {
			strcpy(Ply[fd].extr->tempstr[0], Lockout[i].password);
			return 2;
		}
		else {
			write(fd, "\n\rYour site is locked out.\n\r", 28);
			return 1;
		}
	}

	return 0;
}

/************************************************************************/
/*				addr_equal				*/
/************************************************************************/

/* This function determines if two internet addresses are equal and	*/
/* allows for wild-cards.						*/

int addr_equal(str1, str2)
char	*str1;
char	*str2;
{

	while(*str1 && *str2) {
		if(*str1 == '*') {
			while(*str2 != '.' && *str2) str2++;
			str1++;
			continue;
		}
		else if(*str1 != *str2)
			return(0);
		str1++; str2++;
	}

	if(!*str1 && !*str2) return(1);
	else return(0);
}

/**********************************************************************/
/*				accept_input			      */
/**********************************************************************/

/* This function is called when a player's socket indicates that there */
/* is input waiting.  The socket is read from, and the input is copied */
/* into that player's input buffer.  If the last character entered is  */
/* a carriage return, then the player's interrupt flag is set high.    */

int accept_input(fd)
int	fd;
{
	char 	buf[128], lastchar;
	int 	i, n, prev, itail, ihead;

	n = read(fd, buf, 127);

	if(n<=0)
		Ply[fd].io->commands = -1;	/* Connection dropped */

	else {
		ihead = Ply[fd].io->ihead;
		lastchar = 0;
		itail = Ply[fd].io->itail;
		for(i=0; i<n; i++) {
			if(buf[i] > 31 || (buf[i] == '\n' && lastchar != '\r')
			    || buf[i] == '\r' || buf[i] == '\b') {
				lastchar = buf[i];
				if(buf[i] == '\r') buf[i] = '\n';
				if(buf[i] == '\n') Ply[fd].io->commands++;
				else if(buf[i] == '\b' && ihead != itail) {
					prev = ihead-1 < 0 ? IBUFSIZE-1:ihead-1;
					if(Ply[fd].io->input[prev] == '%')
						ihead -= 2;
					else
						ihead--;
					if(ihead < 0)
						ihead = IBUFSIZE + ihead;
					continue;
				}
				else if(buf[i] == '\b') continue;
				Ply[fd].io->input[ihead] = buf[i];
				ihead = (ihead + 1) % IBUFSIZE;
				if(ihead == itail)
					itail = (itail + 1) % IBUFSIZE;
				if(buf[i] == '%') {
					Ply[fd].io->input[ihead] = buf[i];
					ihead = (ihead + 1) % IBUFSIZE;
					if(ihead == itail)
						itail = (itail + 1) % IBUFSIZE;
				}
			}
		}
		Ply[fd].io->ihead = ihead;
		Ply[fd].io->itail = itail;
		Ply[fd].io->ltime = time(0);
		if(buf[n-1] == '\n' || buf[n-1] == '\r')
			Ply[fd].io->intrpt |= 1;
		else
			Ply[fd].io->intrpt &= ~1;
	}

	return(0);
}

/**********************************************************************/
/*				output_buf			      */
/**********************************************************************/

/* This function outputs the contents of all players' buffers when that  */
/* player is able to be interrupted, or when that player's output buffer */
/* has reached a specific high-water mark (75% of buffer size).          */

void output_buf()
{
	char	str[20], *pstr;
	int 	i, n;
	int	otail, ohead;

	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].io) {
			otail = Ply[i].io->otail;
			ohead = Ply[i].io->ohead;
			if(ohead == otail)
				continue;
			if(Ply[i].io->commands == -1) {
				disconnect(i);
				continue;
			}
			if(Ply[i].io->intrpt & 1) {
				if(Ply[i].ply)
					if(Ply[i].ply->fd > -1 && 
					   F_ISSET(Ply[i].ply, PNOCMP)) {
						n = write(i, "\n\r", 2);
						if(Spy[i] > -1)
						    write(Spy[i], "\n\r", 2);
					}
				n = write(i, &Ply[i].io->output[otail], 
				 ohead>otail ? ohead-otail:OBUFSIZE-otail);
				if(Spy[i] > -1) write(Spy[i], 
				 &Ply[i].io->output[otail], 
				 ohead>otail ? ohead-otail:OBUFSIZE-otail);
				if(otail > ohead) {
					n+= write(i, Ply[i].io->output, ohead);
					if(Spy[i] > -1) 
					write(Spy[i], Ply[i].io->output, ohead);
				}
/*
				if(n < buflen(ohead, otail, OBUFSIZE))
					merror("output_buf", NONFATAL);
*/
				otail = ohead;
				Ply[i].io->otail = otail;
				if(Ply[i].ply) {
					pstr = ply_prompt(Ply[i].ply);
					n = write(i, pstr, strlen(pstr));
					if(Spy[i] > -1)
						write(Spy[i],pstr,strlen(pstr));
				}
			}
			if(buflen(ohead, otail, OBUFSIZE) > (OBUFSIZE*3)/4) {
				n = write(i, &Ply[i].io->output[otail], 
				 ohead>otail ? ohead-otail:OBUFSIZE-otail);
				if(Spy[i] > -1) write(Spy[i], 
				 &Ply[i].io->output[otail], 
				 ohead>otail ? ohead-otail:OBUFSIZE-otail);
				if(otail > ohead) {
					n+= write(i, Ply[i].io->output, ohead);
					if(Spy[i] > -1)
					write(Spy[i], Ply[i].io->output, ohead);
				}
/*
				if(n < buflen(ohead, otail, OBUFSIZE))
					merror("output_buf", NONFATAL);
*/
				otail = ohead;
				Ply[i].io->otail = otail;
			}
		}
	}
}

/**********************************************************************/
/*				print				      */
/**********************************************************************/

/* This function acts just like printf, except it outputs the     */
/* formatted text string to a given socket's output buffer.  The  */
/* socket number is the first parameter.			  */

void print(fd, fmt, i1, i2, i3, i4, i5, i6)
int 	fd;
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char 	msg[2048];
	char	*fmt2;
	int	i = 0, j = 0, k, n, otail, ohead;
	int	num, loc, ind = -1, len, flags = 0;
	int	arg[6];
	char	type;

	if(fd == -1 || fd > Tablesize)
		return;
	if(!Ply[fd].io)
		return;

	if(Ply[fd].ply) {
		if(F_ISSET(Ply[fd].ply, PDINVI))
			flags |= INV;
		if(F_ISSET(Ply[fd].ply, PDMAGI))
			flags |= MAG;
	}

	len = strlen(fmt);
	fmt2 = (char *)malloc(len+1);
	if(!fmt2)
		merror("print", FATAL);

	arg[0] = i1; arg[1] = i2; arg[2] = i3; 
	arg[3] = i4; arg[4] = i5; arg[5] = i6;

	/* Check for %m, %M, %i and %I and modify arguments as necessary */
	do {
		if(fmt[i] == '%') {
			fmt2[j++] = fmt[i];
			num = 0; k = i;
			do {
				k++;
				if((fmt[k] >= 'a' && fmt[k] <= 'z') ||
				   (fmt[k] >= 'A' && fmt[k] <= 'Z') ||
				   fmt[k] == '%') {
					loc = k;
					type = fmt[k];
					break;
				}
				else if(fmt[k] >= '0' && fmt[k] <= '9')
					num = num*10 + fmt[k]-'0';
			} while(k < len);
			if(type == '%') {
				fmt2[j++] = '%';
				i++; i++;
				continue;
			}
			ind++;
			if(type != 'm' && type != 'M' &&
			   type != 'i' && type != 'I') {
				i++;
				continue;
			}

			i = loc + 1;
			fmt2[j++] = 's';

			switch(type) {
			case 'm':
				arg[ind] = (int)crt_str(arg[ind], num, flags);
				continue;
			case 'M':
				arg[ind] = (int)crt_str(arg[ind], num, 
							flags|CAP);
				continue;
			case 'i':
				arg[ind] = (int)obj_str(arg[ind], num, flags);
				continue;
			case 'I':
				arg[ind] = (int)obj_str(arg[ind], num,
							flags|CAP);
				continue;
			}
		}
		fmt2[j++] = fmt[i++];
	} while (i < len);

	fmt2[j] = 0;

	sprintf(msg, fmt2, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5]);
	free(fmt2);
	n = strlen(msg);
	if(n > 78) {
		delimit(msg);
		n = strlen(msg);
	}

	ohead = Ply[fd].io->ohead;
	otail = Ply[fd].io->otail;

	for(i=0; i<n; i++) {
		Ply[fd].io->output[ohead] = msg[i];
		ohead = (ohead + 1) % OBUFSIZE;
		if(ohead == otail)
			otail = (otail + 1) % OBUFSIZE;
		if(msg[i] == '\n') {
			Ply[fd].io->output[ohead] = '\r';
			ohead = (ohead + 1) % OBUFSIZE;
			if(ohead == otail)
				otail = (otail + 1) % OBUFSIZE;
		}
	}
	Ply[fd].io->ohead = ohead;
	Ply[fd].io->otail = otail;
}

/**********************************************************************/
/*				handle_commands			      */
/**********************************************************************/

/* This function strips out the first command in each player's input */
/* buffer, and then sends that command to the player's next function */
/* of input with the appropriate parameter.			     */

void handle_commands()
{
	int	i, j;
	int	itail, ihead;
	char	buf[IBUFSIZE+1];
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].io) {
			if(Ply[i].io->commands == -1) {
				disconnect(i);
				continue;
			}
			if(!Ply[i].io->commands) continue;
			itail = Ply[i].io->itail;
			ihead = Ply[i].io->ihead;
			if(itail == ihead) continue;
			for(j=0; j<IBUFSIZE; j++) {
				if(itail == ihead) {
					buf[j] = 0;
					break;
				}
				if(Ply[i].io->input[itail] == 13 ||
				   Ply[i].io->input[itail] == 10) {
					itail = (itail + 1) % IBUFSIZE;
					buf[j] = 0;
					break;
				}
				buf[j] = Ply[i].io->input[itail];
				itail = (itail + 1) % IBUFSIZE;
			}
			Ply[i].io->itail = itail;
			Ply[i].io->commands--;
			if(Spy[i] > -1) {
				write(Spy[i], buf, strlen(buf));
				write(Spy[i], "\n\r", 2);
			}
			(*Ply[i].io->fn) (i, Ply[i].io->fnparam, buf);
		}
	}
}

/**********************************************************************/
/*				disconnect			      */
/**********************************************************************/

/* This function drops the connection to the player on the socket specified */
/* by the first parameter, clears his spot in the socket bit-array, and     */
/* removes him from the player array by freeing all memory taken by him.    */

void disconnect(fd)
int 	fd;
{
	int 	i;
	etag	*ign, *temp;
	wq_tag	*wq;

	close(fd);
	FD_CLR(fd, &Sockets);

	Spy[fd] = -1;

	if(Ply[fd].io) {
		if(Ply[fd].io->intrpt & 2)
			Numplayers--;
		free(Ply[fd].io);
		Ply[fd].io = 0;
	}
	if(Ply[fd].extr) {
		ign = Ply[fd].extr->first_ignore;
		while(ign) {
			temp = ign;
			ign = ign->next_tag;
			free(temp);
		}
		free(Ply[fd].extr);
		Ply[fd].extr = 0;
	}
	if(Ply[fd].ply) {
		if(F_ISSET(Ply[fd].ply, PSPYON)) {
			for(i=0; i<Tablesize; i++)
				if(Spy[i] == fd) Spy[i] = -1;
			F_CLR(Ply[fd].ply, PSPYON);
		}
		if(Ply[fd].ply->fd > -1) {
			uninit_ply(Ply[fd].ply);
			save_ply(Ply[fd].ply->name, Ply[fd].ply);
		}
		free_crt(Ply[fd].ply);
		Ply[fd].ply = 0;
	}
	else {
		for(wq = First_wait, i=1; wq; wq = wq->next_tag, i++)
			if(wq->fd == fd) {
				remove_wait(i);
				break;
			}
	}
	if(Numwaiting && Numplayers < MAXPLAYERS) {
		i = remove_wait(1);
		if ( i != -1){
		print(i, "%c", 7);
		init_connect(i);
		}
	}
}

/**********************************************************************/
/*				broadcast			      */
/**********************************************************************/

/* This function broadcasts a message to all the players that are in the */
/* game.  If they have the NO-BROADCAST flag set, then they will not see */
/* it.									 */

void broadcast(fmt, i1, i2, i3, i4, i5, i6)
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char	fmt2[1024];
	int	i;

	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].ply)
			if(!F_ISSET(Ply[i].ply, PNOBRD) && Ply[i].ply->fd > -1)
				print(i, fmt2, i1, i2, i3, i4, i5, i6);
	}
}

/**********************************************************************/
/*				broadcast_wiz			      */
/**********************************************************************/

/* This function broadcasts a message to all the DM's who are on at the */
/* time.								*/

void broadcast_wiz(fmt, i1, i2, i3, i4, i5, i6)
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char	fmt2[1024];
	int	i;

	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].ply)
			if(Ply[i].ply->fd > -1 && Ply[i].ply->class >= CARETAKER){
				ANSI(i,YELLOW);
				print(i, fmt2, i1, i2, i3, i4, i5, i6); 
				ANSI(i,WHITE);
			}
	}
}

/**********************************************************************/
/*				broadcast_eaves			      */
/**********************************************************************/

/* This function broadcasts a message to all the DM's who are on at the */
/* time and have the eavesdropping flag set.			 	*/

void broadcast_eaves(fmt, i1, i2, i3, i4, i5, i6)
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char	fmt2[1024];
	int	i;

	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].ply)
			if(Ply[i].ply->fd > -1 &&
			   Ply[i].ply->class >= CARETAKER && 
			   F_ISSET(Ply[i].ply, PEAVES))
				print(i, fmt2, i1, i2, i3, i4, i5, i6);
	}
}

/**********************************************************************/
/*				broadcast_rom			      */
/**********************************************************************/

/* This function outputs a message to everyone in the room specified */
/* by the integer in the second parameter.  If the first parameter   */
/* is greater than -1, then if the player specified by that file     */
/* descriptor is present in the room, he is not given the message    */

void broadcast_rom(ignore, rm, fmt, i1, i2, i3, i4, i5, i6)
int	ignore, rm;
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char	fmt2[1024];
	int	i;

	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].ply)
			if(Ply[i].ply->rom_num == rm && Ply[i].ply->fd > -1
			   && i != ignore )
				print(i, fmt2, i1, i2, i3, i4, i5, i6);
	}
}

/**********************************************************************/
/*				broadcast_rom2			      */
/**********************************************************************/

/* This function is the same as broadcast_rom except that it will ignore */
/* two people in a room.						 */

void broadcast_rom2(ignore1, ignore2, rm, fmt, i1, i2, i3, i4, i5, i6)
int	ignore1, ignore2, rm;
char 	*fmt;
int	i1, i2, i3, i4, i5, i6;
{
	char	fmt2[1024];
	int	i;

	strcpy(fmt2, fmt);
	strcat(fmt2, "\n");
	for(i=0; i<Tablesize; i++) {
		if(FD_ISSET(i, &Sockets) && Ply[i].ply)
			if(Ply[i].ply->rom_num == rm && Ply[i].ply->fd > -1
			   && i != ignore1 && i != ignore2)
				print(i, fmt2, i1, i2, i3, i4, i5, i6);
	}
}

/************************************************************************/
/*				inetname				*/
/************************************************************************/

/* This function returns the internet address of the address structure	*/
/* passed in as the first parameter.					*/

char *inetname(in)
struct in_addr in;
{
	register char *cp=0;
	static char line[50];
	struct hostent *hp;
	struct netent *np;
	static char domain[81];
	static int first = 1;
	int net, lna;

#ifdef GETHOSTBYNAME
	if (first) {
		first = 0;
		if (gethostname(domain, 80) == 0 &&
		    (cp = index(domain, '.')))
			(void) strcpy(domain, cp + 1);
		else
			domain[0] = 0;
	}
	cp = 0;
	if (in.s_addr != INADDR_ANY) {
		net = inet_netof(in);
		lna = inet_lnaof(in);

		if (lna == INADDR_ANY) {
			np = getnetbyaddr(net, AF_INET);
			if (np)
				cp = np->n_name;
		}
		if (cp == 0) {
			hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET);
			if (hp) {
				if ((cp = index(hp->h_name, '.')) &&
				    !strcmp(cp + 1, domain))
					*cp = 0;
				cp = hp->h_name;
			}
		}
	}
#endif
	if (in.s_addr == INADDR_ANY)
		strcpy(line, "*");
	else if (cp)
		strcpy(line, cp);
	else {
		in.s_addr = ntohl(in.s_addr);
#define C(x)	((x) & 0xff)
		sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24),
			C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr));
	}
	return (line);
}

/************************************************************************/
/*				add_wait				*/
/************************************************************************/

/* This function adds the descriptor in the first parameter to the	*/
/* waiting queue.							*/

void add_wait(fd)
int	fd;
{
	wq_tag	*new_wq, *wq;

	new_wq = (wq_tag *)malloc(sizeof(wq_tag));
	new_wq->next_tag = 0;
	new_wq->fd = fd;

	if(!First_wait) {
		First_wait = new_wq;
		Numwaiting = 1;
	}
	else {
		wq = First_wait;
		while(wq->next_tag)
			wq = wq->next_tag;
		wq->next_tag = new_wq;
		Numwaiting++;
	}
	print(fd, "The game is full.\nYou are player #%d in the waiting queue.\n", Numwaiting);
}

/************************************************************************/
/*				remove_wait				*/
/************************************************************************/

/* This function removes the i'th player from the waiting queue.	*/

int remove_wait(i)
int	i;
{
	int	j, fd;
	wq_tag	*wq, *prev;
	long	t;
	char 	str[50];

	wq = First_wait;

/* write is being used here to check to see if the given
  file desc. is still valid.  Theorically, only one write is
  needed, but for some reason 2 writes are needed */

 write(wq->fd," ",1);
        if(i == 1) {
                if (write(wq->fd,"\n",1) == -1){
                fd = -1;
		}
                else
                fd = wq->fd;
                First_wait = wq->next_tag;
        }
	else {
		for(j=1; j<i; j++) {
			prev = wq;
			wq = wq->next_tag;
		}
		fd = wq->fd;
		prev->next_tag = wq->next_tag;
	}

	free(wq);
	Numwaiting--;

	for(wq=First_wait, j=1; wq; wq=wq->next_tag, j++)
		if(j >= i) print(wq->fd, "You are player #%d in the waiting queue.\n", j);

	output_buf();
	return(fd);
}

void waiting(fd, param, str)
int	fd;
int	param;
char	*str;
{
	RETURN(fd, waiting, 1);
}

/************************************************************************/
/*				child_died				*/
/************************************************************************/

/* This function gets called when a SIGCHLD signal is sent to the	*/
/* program.								*/

void child_died()
{
	Deadchildren++;
	signal(SIGCHLD, child_died);
}

/************************************************************************/
/*				reap_children				*/
/************************************************************************/

/* This program goes through and kills off (waits for) each child	*/
/* that has completed processing to avoid zombie processes.		*/
/* If the child was an authentication process, then the information	*/
/* it returned is looked up in the file it saved.			*/

void reap_children()
{
	int pid, i, found, status;
	char filename[127], userid[80], address[80], timestr[80];
	FILE *fp;
	long t;

	t = time(0);
	strcpy(timestr, (char *)ctime(&t));
	timestr[strlen(timestr)-1] = 0;

	while(Deadchildren > 0) {
		Deadchildren--;
		found = -1;
		pid = wait(&status);
		sprintf(filename, "%s/auth/lookup.%d", LOGPATH, pid);
		for(i=0; i<Tablesize; i++) {
			if(Ply[i].io && Ply[i].io->lookup_pid == pid) {
				found = i;
				break;
			}
		}
		if(found < 0) {
			if(file_exists(filename)) unlink(filename);
			continue;
		}
		fp = fopen(filename, "r");
		if(!fp) continue;
		fscanf(fp, "%s %s", userid, address);
		if(strlen(userid))
			userid[8] = 0;
		if(strlen(address))
			address[39] = 0;
		fclose(fp);
		unlink(filename);
		strcpy(Ply[found].io->userid, userid);
		logf("%s: %s@%s (%s@%s) connected.\n", timestr,
			userid, address, userid, Ply[found].io->address);
		if(strcmp(address, "UNKNOWN"))
			strcpy(Ply[found].io->address, address);
	}

	/* just in case, kill off any zombies */
	wait4(-1, &status, WNOHANG, (struct rusage *)0);

}