/
bin/95/
docs/DM/
docs/creatures/
docs/objects/
docs/rooms/
docs/utils/
help/
log/
objmon/ddesc/
player/
post/
rooms/
util/
util/fing/
util/italk/
util/lev-reset/
util/lib/
util/list/
util/msg/
util/tdel/
util/vote/
util/wtfix/
/*
 * IO.C:
 *
 *	Socket input/output/establishment functions.
 *
 *	Copyright (C) 1991, 1992, 1993, 1997 Brooke Paul & Brett Vickers
 *
 */

#ifdef IRIX
	#define _BSD_SIGNALS
	#include <fcntl.h>
	/* #include <stropts.h> */
#endif

#include <stdio.h>
#include <sys/types.h>

#ifndef WIN32
	#include <sys/ioctl.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>
#else
	#include <winsock.h>
#endif

#include <errno.h>
#include <string.h>
#include "mstruct.h"
#include "mextern.h"
#include "version.h"
#ifdef DMALLOC
  #include "/usr/local/include/dmalloc.h"
#endif


#ifdef WIN32
#define ioctl(a,b,c)	ioctlsocket(a,b,c)
#endif

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

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;  



/* 

 C file handles are not always the same as system handles.
 Since read() and write() take C file handles, I had to change this
 for the WIN32 socket io stuff.  Thank god, it's isolated to this file.

*/
#ifdef WIN32
int scwrite(int fh, const void *lpvBuffer, unsigned int nLen)
{
	DWORD dwRet; 
	WriteFile((HANDLE)fh, lpvBuffer, nLen, &dwRet, NULL); 

	if(Spy[fh] > -1) 
		{
		WriteFile((HANDLE)Spy[fh], lpvBuffer, nLen, &dwRet, NULL); 
		}
	
	return(dwRet);

}


int scread(int fh, void *lpvBuffer, unsigned int nLen)
{

	DWORD dwRet; 
	ReadFile((HANDLE)fh, lpvBuffer, nLen, &dwRet, NULL); 
	return(dwRet);
}

#endif

/**********************************************************************/
/*				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, flags;
	extern char		report;
#ifdef WINNT
	int			optval = SO_SYNCHRONOUS_NONALERT;
#endif /* WINNT */

#ifdef WIN32
	/* gotta initialize the winsock stuff */
	WORD wVersionRequested; 
	WSADATA wsaData; 
	int err; 
	wVersionRequested = MAKEWORD(1, 1); 
 
	err = WSAStartup(wVersionRequested, &wsaData); 
	if (err != 0) 
		/* Tell the user that we couldn't find a useable */ 
		/* winsock.dll.     */ 
		return; 
#ifdef WINNT
 	err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&optval,sizeof(optval));
#endif /* WINNT */

#endif /* WIN32 */

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

	Tablesize = getdtablesize();
	if (Tablesize > PMAX){
		Tablesize = PMAX;
		loge("Tablesize greater than PMAX\n");
	}
#else
		Tablesize = PMAX;
#endif

	Waitsock = socket(AF_INET, SOCK_STREAM, 0);
	if(Waitsock < 0)
		exit(-1);

	FD_ZERO(&Sockets);
	FD_SET(Waitsock, &Sockets); 

	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);
	/* added by John */
	addr.sin_family = AF_INET;

	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;
#ifdef IRIX
	flags = fcntl(Waitsock, F_GETFL);
	flags |= O_NONBLOCK;
	fcntl(Waitsock, flags);
#else
	ioctl(Waitsock, FIONBIO, &i);
#endif /* IRIX */
}


/**********************************************************************/
/*				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_sec = 0;	    
	t.tv_usec = 75000L;

#ifdef IRIX
 	if(select(Tablesize, &sockcheck, (fd_set *) 0, (fd_set *) 0, &t) > 0) { 
#else
	if(select(Tablesize, &sockcheck, 0, 0, &t) > 0) {
#endif /* IRIX */
		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));

#ifndef WIN32
#ifdef IRIX
        pid = fork();
#else
        pid = vfork();
#endif /* IRIX */
	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;
	}
#else
		strcpy(io->userid, "unknown");
		io->lookup_pid = pid;
#endif
	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) {
			#ifdef WIN32
				scwrite(fd, "Queue full.\n\r", 13);
			#else
				write(fd, "Queue full.\n\r", 13);
			#endif /* WIN32 */

			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;


	/* Put your MUD name here */
	print(fd,"\n%s", title);

/********************************************************************/
/*  As part of the copyright agreement this section must be left    */
/* intact.  Please give credit where credit is due, and feel free to */
/* add your name once you begin adding to the Mordor codebase.      */
	print(fd, "\n  Mordor v%s", VERSION);
	print(fd, "\nProgrammed by:");
	print(fd, "\n  Brooke Paul & Brett Vickers");
					/* brooke@nazgul.com */
	print(fd, "\nContributions by:");
	print(fd, "\n  Paul Telford");
					/* pdtelford@pobox.com */
#ifdef WIN32
	print(fd, "\nWIN32 by:\n  John Freeman & Eric Krichbaum"); 
					/* ekrich@iolinc.net */
#endif /* WIN32 */
/*							            */
/********************************************************************/
	Ply[fd].io->intrpt |= 2;
	Numplayers++;

	if((i = locked_out(fd)) == 2) {
		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(!strcmp(Lockout[i].userid, "all"))
		if(Lockout[i].password[0]) {
			strcpy(Ply[fd].extr->tempstr[0], Lockout[i].password);
			return 2;
		}
		else {
			#ifdef WIN32
			scwrite(fd, "\n\rYour site is locked out.\n\r", 28);
			#else	
			write(fd, "\n\rYour site is locked out.\n\r", 28);
			#endif /* WIN32 */

			print(fd, "\n\rSend questions to %s.\n\r", questions_to_email);
			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;

#ifdef WIN32
	n = scread(fd, buf, 127);
#else
	n = read(fd, buf, 127);
#endif

	if(n<=0) {
		Ply[fd].io->commands = -1;	/* Connection dropped */
		disconnect(fd);
	}
	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)) {
					#ifdef WIN32	
						n = scwrite(i, "\n\r", 2);
					#else
						n = write(i, "\n\r", 2);
						if(Spy[i] > -1)
							write(Spy[i], "\n\r", 2);
					#endif /* WIN32 */

					}
				#ifdef WIN32
				n = scwrite(i, &Ply[i].io->output[otail], 
					ohead>otail ? ohead-otail:OBUFSIZE-otail);
				#else
				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);        
				#endif /* WIN32 */
				if(otail > ohead) {
				#ifdef WIN32	
					n+= scwrite(i, Ply[i].io->output, ohead);
				#else
					n+= write(i, Ply[i].io->output, ohead);
					if(Spy[i] > -1)
						write(Spy[i], Ply[i].io->output, ohead);
				#endif /* WIN32 */
				}

				/* 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);
					#ifdef WIN32
					n = scwrite(i, pstr, strlen(pstr));
					#else
					n = write(i, pstr, strlen(pstr));
					if(Spy[i] > -1)
						write(Spy[i],pstr,strlen(pstr));
					#endif /* WIN32 */
				}
			}
			if(buflen(ohead, otail, OBUFSIZE) > (OBUFSIZE*3)/4) {
				#ifdef WIN32
				n = scwrite(i, &Ply[i].io->output[otail], 
				 ohead>otail ? ohead-otail:OBUFSIZE-otail);
				#else	
				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);
				#endif /* WIN32 */

				if(otail > ohead) {
					#ifdef WIN32
					n+= scwrite(i, Ply[i].io->output, ohead);
					#else
					n+= write(i, Ply[i].io->output, ohead);
					if(Spy[i] > -1)  
						write(Spy[i], Ply[i].io->output, ohead);
					#endif /* WIN32 */
				}

				/* 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[4096];
	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 < 0 || 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) {
				#ifdef WIN32
				scwrite(Spy[i], buf, strlen(buf));
				scwrite(Spy[i], "\n\r", 2);
				#else
				write(Spy[i], buf, strlen(buf));
				write(Spy[i], "\n\r", 2);
				#endif /* WIN32 */
			}
			(*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;
	ctag	*crm, *ctemp;

#ifdef WIN32
	CloseHandle(fd);
#else
	close(fd);
#endif
	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);
		}
		crm = Ply[fd].extr->first_charm;
		while(crm) {
			ctemp = crm;
			crm = crm->next_tag;
			free(ctemp);
		}
		if(Ply[fd].extr->alias_crt) { 
			F_CLR(Ply[fd].extr->alias_crt, MDMFOL);
			Ply[fd].extr->alias_crt = 0;
		}
		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_login                       */
/**********************************************************************/

/* 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_login(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, PNLOGN) && 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 && !F_ISSET(Ply[i].ply, PNOBRD)){
				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;
#ifndef WIN32
	if(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;
					(const)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 */
 #ifdef WIN32
   scwrite(wq->fd," ",1);
 #else
   write(wq->fd," ",1);
 #endif /* WIN32 */
        if(i == 1) {
		#ifdef WIN32
		if (scwrite(wq->fd,"\n",1) == -1){
		#else
		if (write(wq->fd,"\n",1) == -1){
		#endif /* WIN32 */
                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++;

#ifndef WIN32
	signal(SIGCHLD, child_died);
#endif
}
/***********************************************************************
	
*	This causes the game to shutdown in one minute.  It is used
*  by signal to force a  shutdown in response to a HUP signal 
*  (i.e. kill -HUP pid) from the system.
*/
void quick_shutdown()
{
        Shutdown.ltime = time(0);   
        Shutdown.interval = 60;
}

/************************************************************************/
/*				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()
{
#ifndef WIN32
	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);
		loge("%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 */	
	wait3(&status, WNOHANG, (struct rusage *)0);
	/* Try this wait if wait3 doesnt work */
	/*wait4(-1, &status, WNOHANG, (struct rusage *)0); */
#endif
}

/**********************************************************************/
/*				broadcast_class									      */
/**********************************************************************/

/* This function broadcasts a message to all the same class on at the */
/* time.								*/

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

	strcpy(fmt2, "(");
	strcat(fmt2, class_str[ply_ptr->class]);
	strcat(fmt2, ") ");	
	strcat(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,PIGCLA) && Ply[i].ply->fd > -1 && Ply[i].ply->class == ply_ptr->class && !F_ISSET(Ply[i].ply, PNOBRD)){
					ANSI(i,YELLOW);
					print(i, fmt2, i1, i2, i3, i4, i5, i6); 
					ANSI(i,WHITE);
			}
	}
}

/* Doneval: Here a little function to "ask" somthing with ANSI codes. */
void ask_for(fd, fmt, i1, i2, i3, i4, i5, i6)
int     fd;
char    *fmt;
int     i1, i2, i3, i4 ,i5 ,i6;
{
        char    str[80];

	print(fd, "\n");
        gotoxy(fd, 24, 1);

        print(fd, fmt, i1, i2, i3, i4, i5, i6);
		sprintf(str, "%c[24;%dH", 27, strlen(fmt));
        write(fd, str, strlen(str));
/*
        if(F_ISSET(Ply[fd].ply, PANSIC)) 
			sprintf(str, "%c[24;%dH", 27, strlen(fmt));
        print(fd, str);
*/
}

/**********************************************************************/
/*                      disconnect_all_ply                            */
/**********************************************************************/

/* This function disconnects all players.                             */
/* Doneval: Perhaps this function is not necessary. */
void disconnect_all_ply()
{
        int i;

        for(i=0; i<Tablesize; i++) {
                if(Ply[i].ply && Ply[i].io && Ply[i].ply->name[0])
                        disconnect(Ply[i].ply->fd);
        }
}