1stMUD/corefiles/
1stMUD/gods/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/

/*
 * This file contains all of the OS-dependent stuff:
 *   startup, signals, BSD sockets for tcp/ip, i/o, timing.
 *
 * The data flow for input is:
 *    Game_loop ---> Read_from_descriptor ---> Read
 *    Game_loop ---> Read_from_buffer
 *
 * The data flow for output is:
 *    Game_loop ---> Process_Output ---> Write_to_descriptor -> Write
 *
 * The OS-dependent functions are Read_from_descriptor and Write_to_descriptor.
 * -- Furey  26 Jan 1993
 */

#include <sys/types.h>
#if !defined(WIN32)
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>				/* OLC -- for close read write etc */
#else
#include <winsock2.h>
#include <sys\timeb.h>
#include <direct.h>
#include <io.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>				/* chprintf */
#include "merc.h"
#include "interp.h"
#include "recycle.h"
#include "tables.h"
#if defined(WIN32)
#include "../win32/winstuff.h"
#endif

/*
 * Malloc debugging stuff.
 */
#if defined(sun)
#undef MALLOC_DEBUG
#endif

#if defined(MALLOC_DEBUG)
#include <malloc.h>
extern int malloc_debug args ((int));
extern int malloc_verify args ((void));
#endif

/*
 * Signal handling.
 * Apollo has a problem with __attribute(atomic) in signal.h,
 *   I dance around it.
 */
#if defined(apollo)
#define __attribute(x)
#endif

#include <signal.h>

#if defined(apollo)
#undef __attribute
#endif

#include <fcntl.h>
#if !defined(WIN32)
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif
#include "telnet.h"

/*
 * OS-dependent declarations.
 */
#if	defined(_AIX)
#include <sys/select.h>
int accept args ((int s, struct sockaddr * addr, int *addrlen));
int bind args ((int s, struct sockaddr * name, int namelen));
void bzero args ((char *b, int length));
int getpeername args ((int s, struct sockaddr * name, int *namelen));
int getsockname args ((int s, struct sockaddr * name, int *namelen));
int gettimeofday args ((struct timeval * tp, struct timezone * tzp));
int listen args ((int s, int backlog));
int setsockopt
args ((int s, int level, int optname, void *optval, int optlen));
int socket args ((int domain, int type, int protocol));
#endif

#if	defined(apollo)
#include <unistd.h>
void bzero args ((char *b, int length));
#endif

#if	defined(__hpux)
int accept args ((int s, void *addr, int *addrlen));
int bind args ((int s, const void *addr, int addrlen));
void bzero args ((char *b, int length));
int getpeername args ((int s, void *addr, int *addrlen));
int getsockname args ((int s, void *name, int *addrlen));
int gettimeofday args ((struct timeval * tp, struct timezone * tzp));
int listen args ((int s, int backlog));
int setsockopt
args ((int s, int level, int optname, const void *optval, int optlen));
int socket args ((int domain, int type, int protocol));
#endif

#if	defined(interactive)
#include <net/errno.h>
#include <sys/fnctl.h>
#endif

#if	defined(MIPS_OS)
extern int errno;
#endif

#if	defined(NeXT)
int close args ((int fd));
int fcntl args ((int fd, int cmd, int arg));
#if	!defined(htons)
u_short htons args ((u_short hostshort));
#endif
#if	!defined(ntohl)
u_long ntohl args ((u_long hostlong));
#endif
int read args ((int fd, char *buf, int nbyte));
int select
args ((int width, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
	   struct timeval * timeout));
int write args ((int fd, char *buf, int nbyte));
#endif

#if	defined(sequent)
int accept args ((int s, struct sockaddr * addr, int *addrlen));
int bind args ((int s, struct sockaddr * name, int namelen));
int close args ((int fd));
int fcntl args ((int fd, int cmd, int arg));
int getpeername args ((int s, struct sockaddr * name, int *namelen));
int getsockname args ((int s, struct sockaddr * name, int *namelen));
int gettimeofday args ((struct timeval * tp, struct timezone * tzp));
#if	!defined(htons)
u_short htons args ((u_short hostshort));
#endif
int listen args ((int s, int backlog));
#if	!defined(ntohl)
u_long ntohl args ((u_long hostlong));
#endif
int read args ((int fd, char *buf, int nbyte));
int select
args ((int width, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
	   struct timeval * timeout));
int setsockopt
args ((int s, int level, int optname, caddr_t optval, int optlen));
int socket args ((int domain, int type, int protocol));
int write args ((int fd, char *buf, int nbyte));
#endif

/* This includes Solaris Sys V as well */
#if defined(sun)
int accept args ((int s, struct sockaddr * addr, int *addrlen));
int bind args ((int s, struct sockaddr * name, int namelen));
void bzero args ((char *b, int length));
int close args ((int fd));
int getpeername args ((int s, struct sockaddr * name, int *namelen));
int getsockname args ((int s, struct sockaddr * name, int *namelen));
int listen args ((int s, int backlog));
int read args ((int fd, char *buf, int nbyte));
int select
args ((int width, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
	   struct timeval * timeout));

#if !defined(__SVR4)
int gettimeofday args ((struct timeval * tp, struct timezone * tzp));

#if defined(SYSV)
int setsockopt
args ((int s, int level, int optname, const char *optval, int optlen));
#else
int setsockopt
args ((int s, int level, int optname, void *optval, int optlen));
#endif
#endif
int socket args ((int domain, int type, int protocol));
int write args ((int fd, char *buf, int nbyte));
#endif

#if defined(ultrix)
int accept args ((int s, struct sockaddr * addr, int *addrlen));
int bind args ((int s, struct sockaddr * name, int namelen));
void bzero args ((char *b, int length));
int close args ((int fd));
int getpeername args ((int s, struct sockaddr * name, int *namelen));
int getsockname args ((int s, struct sockaddr * name, int *namelen));
int gettimeofday args ((struct timeval * tp, struct timezone * tzp));
int listen args ((int s, int backlog));
int read args ((int fd, char *buf, int nbyte));
int select
args ((int width, fd_set * readfds, fd_set * writefds, fd_set * exceptfds,
	   struct timeval * timeout));
int setsockopt
args ((int s, int level, int optname, void *optval, int optlen));
int socket args ((int domain, int type, int protocol));
int write args ((int fd, char *buf, int nbyte));
#endif

/*
 * Global variables.
 */
DESCRIPTOR_DATA *descriptor_list;	/* All open descriptors     */
DESCRIPTOR_DATA *d_next;		/* Next descriptor in loop  */
FILE *fpReserve;				/* Reserved file handle     */
bool god;						/* All new chars are gods!  */
bool merc_down;					/* Shutdown         */
char str_boot_time[MAX_INPUT_LENGTH];
time_t current_time;			/* time of this pulse */
bool MOBtrigger = TRUE;			/* act() switch                 */
/* Needs to be global because of do_copyover */
int port, control;
/* Global variable */
volatile sig_atomic_t crashed = 0;	/* Are we currently crashing? */

#if	defined( WIN32 )
#define WOULD_HAVE_BLOCKED ( WSAGetLastError() == WSAEWOULDBLOCK )
#else
#define WOULD_HAVE_BLOCKED ( errno == EWOULDBLOCK )
#endif

void game_loop args ((int ctrl));
int init_socket args ((int prt));
void init_descriptor args ((int ctrl));
bool read_from_descriptor args ((DESCRIPTOR_DATA * d));
bool write_to_descriptor args ((DESCRIPTOR_DATA * d, char *txt, int length));
void halt_mud args ((int sig));

/*
 * Other local functions (OS-independent).
 */
int main args ((int argc, char **argv));
void nanny args ((DESCRIPTOR_DATA * d, const char *argument));
bool process_output args ((DESCRIPTOR_DATA * d, bool fPrompt));
void read_from_buffer args ((DESCRIPTOR_DATA * d));
void stop_idling args ((CHAR_DATA * ch));
void bust_a_prompt args ((CHAR_DATA * ch));

#if !defined(WIN32)
#if !defined(__CYGWIN__)
// Stop an endless loop 
// Uses alarm() to check for game clock advance every 300 seconds.  If more
// than 240 seconds of realtime has elapsed, assume loop and conduct a copyover.
static void sigalrm (int sig)
{
	time_t ptm;
	time (&ptm);
	fprintf (stderr, "TOCK!\n\r");

	if ((ptm - current_time) > 240)
	{
		fprintf (stderr, "Looping - Last Command: %s\r", last_command);
		// this requires you to add an "if (ch)" before the chprint
		// statements in do_copyover.
		do_copyover (NULL, "");
		exit (1);
	}
	alarm (300);
}
#endif
#else
void gettimeofday (struct timeval *t, void *tz)
{
	struct timeb timebuffer;
	ftime (&timebuffer);
	t->tv_sec = timebuffer.time;
	t->tv_usec = timebuffer.millitm * 1000;
}

#endif

int main (int argc, char **argv)
{
	struct timeval now_time;
	bool fCopyOver = FALSE;
#if !defined(WIN32) && !defined(__CYGWIN__)
	struct sigaction halt_action, ignore_action, alarm_action;

	halt_action.sa_handler = halt_mud;
	sigemptyset (&halt_action.sa_mask);
	halt_action.sa_flags = SA_NOMASK;

	ignore_action.sa_handler = SIG_IGN;
	sigemptyset (&ignore_action.sa_mask);
	ignore_action.sa_flags = 0;

	alarm_action.sa_handler = sigalrm;
	sigemptyset (&alarm_action.sa_mask);
	alarm_action.sa_flags = SA_NOMASK;

	sigaction (SIGPIPE, &ignore_action, NULL);	/* who cares about pipes? */
	sigaction (SIGHUP, &ignore_action, NULL);	/* stay alive if user quits */
	sigaction (SIGINT, &halt_action, NULL);	/* interrupted at keyboard */
	sigaction (SIGQUIT, &halt_action, NULL);	/* quit at keyboard */
	sigaction (SIGILL, &halt_action, NULL);	/* illegal instruction */
	sigaction (SIGFPE, &halt_action, NULL);	/* floating point error */
	sigaction (SIGSEGV, &halt_action, NULL);	/* invalid memory reference */
	sigaction (SIGTERM, &halt_action, NULL);	/* terminate */
	sigaction (SIGBUS, &halt_action, NULL);	/* out of memory?? */
	sigaction (SIGALRM, &alarm_action, NULL);	/* endless loop check */

	alarm (300);
#endif
	/*
	 * Memory debugging if needed.
	 */
#if defined(MALLOC_DEBUG)
	malloc_debug (2);
#endif

	/*
	 * Init time.
	 */
	gettimeofday (&now_time, NULL);
	current_time = (time_t) now_time.tv_sec;
	strcpy (str_boot_time, ctime (&current_time));

	/*
	 * Reserve one channel for our use.
	 */
	if ((fpReserve = file_open (NULL_FILE, "r")) == NULL)
	{
		perror (NULL_FILE);
		exit (1);
	}

	/*
	 * Get the port number.
	 */
	port = 4000;
	if (argc > 1)
	{
		if (!is_number (argv[1]))
		{
			fprintf (stderr, "Usage: %s [port #]\n", argv[0]);
			exit (1);
		}
		else if ((port = atoi (argv[1])) <= 1024)
		{
			fprintf (stderr, "Port number must be above 1024.\n");
			exit (1);
		}
		/* Are we recovering from a copyover? */
		if (argv[2] && argv[2][0])
		{
			fCopyOver = TRUE;
			control = atoi (argv[3]);
		}
		else
			fCopyOver = FALSE;
	}

#if defined(WIN32)
	{
		/* Initialise Windows sockets library */

		unsigned short wVersionRequested = MAKEWORD (1, 1);
		WSADATA wsadata;
		int err;

		/* Need to include library: wsock32.lib for Windows Sockets */
		err = WSAStartup (wVersionRequested, &wsadata);
		if (err)
		{
			fprintf (stderr, "Error %i on WSAStartup\n", err);
			exit (1);
		}
	}
#endif

	/*
	 * Run the game.
	 */

	if (!fCopyOver)
		control = init_socket (port);
	boot_db ();
	sprintf (log_buf, "ROM is ready to rock on port %d.", port);
	log_string (log_buf);
	if (fCopyOver)
		copyover_recover ();
	game_loop (control);
	close (control);

	/*
	 * That's all, folks.
	 */
	log_string ("Normal termination of game.");
	exit (0);
	return 0;
}

int init_socket (int prt)
{
	static struct sockaddr_in sa_zero;
	struct sockaddr_in sa;
	int x = 1;
	int fd;

	if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
	{
		perror ("Init_socket: socket");
		exit (-1);
	}

	if (setsockopt
		(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof (x)) < 0)
	{
		perror ("Init_socket: SO_REUSEADDR");
		close (fd);
		exit (-1);
	}

#if defined(SO_DONTLINGER) && !defined(SYSV)
	{
		struct linger ld;

		ld.l_onoff = 1;
		ld.l_linger = 1000;

		if (setsockopt
			(fd, SOL_SOCKET, SO_DONTLINGER, (char *) &ld, sizeof (ld)) < 0)
		{
			perror ("Init_socket: SO_DONTLINGER");
			close (fd);
			exit (-1);
		}
	}
#endif

	sa = sa_zero;
	sa.sin_family = AF_INET;
	sa.sin_port = htons (prt);

	if (bind (fd, (struct sockaddr *) &sa, sizeof (sa)) < 0)
	{
		perror ("Init socket: bind");
		close (fd);
		exit (-1);
	}

	if (listen (fd, 3) < 0)
	{
		perror ("Init socket: listen");
		close (fd);
		exit (-1);
	}

	return fd;
}

void show_greeting (DESCRIPTOR_DATA * dnew)
{
	extern const char *help_greeting;
	if (help_greeting[0] == '.')
		write_to_buffer (dnew, help_greeting + 1, 0);
	else
		write_to_buffer (dnew, help_greeting, 0);
}

#define MS_PER_PULSE ( 1000 / PULSE_PER_SECOND )
// maintained by SyncGameClock; currently read only by WaitForPulse()
unsigned long last_pulse_millisec;

void SynchronizeClock (void)
{
#if defined( WIN32 )
	struct _timeb now_time;

	_ftime (&now_time);
	current_time = now_time.time;
	last_pulse_millisec = now_time.millitm;
#else
	struct timeval last_time;

	gettimeofday (&last_time, NULL);
	current_time = (time_t) last_time.tv_sec;
	last_pulse_millisec = (unsigned long) last_time.tv_usec / 1000;
#endif
}

#if !defined(WIN32)
void Sleep (time_t milliseconds)
{
	struct timeval nap_time;
	nap_time.tv_usec = 1000 * milliseconds;
	nap_time.tv_sec = 0;
	if (select (0, NULL, NULL, NULL, &nap_time) < 0)
	{
		switch (errno)
		{
		case EBADF:
			bug ("Invalid file descriptor passed to Select()", 0);
			break;
		case EINTR:			//bugf("A non-blocked signal was caught.", 0);
			break;
		case EINVAL:
			bug ("Negative \'n\' descriptor passed to Select()", 0);
			break;
		case ENOMEM:
			bug
				("Select() was unable to allocate memory for internal tables.",
				 0);
			break;
		default:
			bug ("Unknown error.", 0);
			break;
		}
	}
}
#endif

void WaitForPulse ()
{
	signed long pulse;
	signed long nap;
#if !defined(WIN32)
	struct timeval now_time;

	gettimeofday (&now_time, NULL);
	pulse = ((int) now_time.tv_sec) - ((int) current_time);
	// Careful here of signed versus unsigned arithmetic.
	pulse *= 1000;
	pulse += ((int) now_time.tv_usec / 1000) - ((int) last_pulse_millisec);
#else
	struct _timeb now_time;

	_ftime (&now_time);
	pulse = now_time.time - current_time;
	pulse *= 1000;
	pulse += now_time.millitm - last_pulse_millisec;
#endif
	nap = MS_PER_PULSE - pulse;

	if (nap > 0)
	{
		Sleep (nap);
	}
}

void game_loop (int ctrl)
{
	static struct timeval null_time;
	struct timeval last_time;

#if !defined(WIN32)
	signal (SIGPIPE, SIG_IGN);
#endif
	gettimeofday (&last_time, NULL);
	current_time = (time_t) last_time.tv_sec;

	/* Main loop */
	while (!merc_down)
	{
		fd_set in_set;
		fd_set out_set;
		fd_set exc_set;
		DESCRIPTOR_DATA *d;
		int maxdesc;

#if defined(MALLOC_DEBUG)
		if (malloc_verify () != 1)
			abort ();
#endif

		/*
		 * Poll all active descriptors.
		 */
		FD_ZERO (&in_set);
		FD_ZERO (&out_set);
		FD_ZERO (&exc_set);
		FD_SET (ctrl, &in_set);
		maxdesc = ctrl;
		for (d = descriptor_list; d; d = d->next)
		{
			maxdesc = UMAX (maxdesc, d->descriptor);
			FD_SET (d->descriptor, &in_set);
			FD_SET (d->descriptor, &out_set);
			FD_SET (d->descriptor, &exc_set);
		}

		if (select (maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) < 0)
		{
			if (errno != EINTR)
			{
				perror ("Game_loop: select: poll");
				exit (1);
			}
		}

		/*
		 * New connection?
		 */
		if (FD_ISSET (ctrl, &in_set))
			init_descriptor (ctrl);

		/*
		 * Kick out the freaky folks.
		 */
		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;
			if (FD_ISSET (d->descriptor, &exc_set))
			{
				FD_CLR (d->descriptor, &in_set);
				FD_CLR (d->descriptor, &out_set);
				if (d->character && d->connected == CON_PLAYING)
					save_char_obj (d->character);
				d->outtop = 0;
				close_socket (d);
			}
		}

		/*
		 * Process input.
		 */
		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;
			d->fcommand = FALSE;

			if (FD_ISSET (d->descriptor, &in_set))
			{
				if (d->character != NULL)
					d->character->timer = 0;
				if (!read_from_descriptor (d))
				{
					FD_CLR (d->descriptor, &out_set);
					if (d->character != NULL && d->connected == CON_PLAYING)
						save_char_obj (d->character);
					d->outtop = 0;
					close_socket (d);
					continue;
				}
			}

			if (d->character != NULL && d->character->daze > 0)
				--d->character->daze;

			if (d->character != NULL && d->character->wait > 0)
			{
				--d->character->wait;
				continue;
			}

			read_from_buffer (d);
			if (d->incomm[0] != '\0')
			{
				d->fcommand = TRUE;
				stop_idling (d->character);

				/* OLC */
				if (d->showstr_point)
					show_string (d, d->incomm);
				else if (d->pString)
					string_add (d->character, d->incomm);
				else
					switch (d->connected)
					{
					case CON_PLAYING:
						if (!run_olc_editor (d))
							substitute_alias (d, d->incomm);
						break;
					default:
						nanny (d, d->incomm);
						break;
					}

				d->incomm[0] = '\0';
			}
		}

		/*
		 * Autonomous game motion.
		 */
		update_handler ();

		/*
		 * Output.
		 */
		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;

			if ((d->fcommand || d->outtop > 0
#if !defined(NO_MCCP)
				 || d->out_compress) &&
#else
				) &&
#endif
				FD_ISSET (d->descriptor, &out_set))
			{
				bool ok = TRUE;

				if (d->fcommand || d->outtop > 0)
					ok = process_output (d, TRUE);

#if !defined(NO_MCCP)
				if (ok && d->out_compress)
					ok = processCompressed (d);
#endif

				if (!ok)
				{
					if (d->character != NULL && d->connected == CON_PLAYING)
						save_char_obj (d->character);
					d->outtop = 0;
					close_socket (d);
				}
			}
		}

		/*
		 * Synchronize to a clock.
		 * Sleep( last_time + 1/PULSE_PER_SECOND - now ).
		 * Careful here of signed versus unsigned arithmetic.
		 */
		WaitForPulse ();
		SynchronizeClock ();
	}

	return;
}

void init_descriptor (int ctrl)
{
	char buf[MAX_STRING_LENGTH];
	DESCRIPTOR_DATA *dnew;
	struct sockaddr_in sock;
	struct hostent *from;
	int desc;
#if defined(__CYGWIN__)
	int size;
#else
	unsigned int size;
#endif
#if defined(WIN32)
	static unsigned long ARGP = 1;
#endif

	size = sizeof (sock);

	getsockname (ctrl, (struct sockaddr *) &sock, &size);

	if ((desc = accept (ctrl, (struct sockaddr *) &sock, &size)) < 0)
	{
		perror ("New_descriptor: accept");
		return;
	}

#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

#if defined( WIN32 )

	if (ioctlsocket (desc, FIONBIO, &ARGP) != 0)
	{
		bugf ("ioctlsocket returned error code %d", WSAGetLastError ());
		return;
	}
#else

	if (fcntl (desc, F_SETFL, FNDELAY) == -1)
	{
		perror ("New_descriptor: fcntl: FNDELAY");
		return;
	}
#endif
	/*
	 * Cons a new descriptor.
	 */
	dnew = new_descriptor ();	/* new_descriptor now also allocates things */
	dnew->descriptor = desc;

	size = sizeof (sock);
	if (getpeername (desc, (struct sockaddr *) &sock, &size) < 0)
	{
		perror ("New_descriptor: getpeername");
		dnew->host = str_dup ("(unknown)");
	}
	else
	{
		/*
		 * Would be nice to use inet_ntoa here but it takes a struct arg,
		 * which ain't very compatible between gcc and system libraries.
		 */
		int addr;

		addr = ntohl (sock.sin_addr.s_addr);
		sprintf (buf, "%d.%d.%d.%d", (addr >> 24) & 0xFF,
				 (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, (addr) & 0xFF);
		sprintf (log_buf, "Sock.sinaddr:  %s", buf);
		log_string (log_buf);
		from =
			gethostbyaddr ((char *) &sock.sin_addr,
						   sizeof (sock.sin_addr), AF_INET);
		dnew->host = str_dup (from ? from->h_name : buf);
	}

	/*
	 * Swiftest: I added the following to ban sites.  I don't
	 * endorse banning of sites, but Copper has few descriptors now
	 * and some people from certain sites keep abusing access by
	 * using automated 'autodialers' and leaving connections hanging.
	 *
	 * Furey: added suffix check by request of Nickel of HiddenWorlds.
	 */
	if (check_ban (dnew->host, BAN_ALL))
	{
		write_to_descriptor (dnew,
							 "Your site has been banned from this mud.\n\r",
							 0);
		close (desc);
		free_descriptor (dnew);
		return;
	}
	/*
	 * Init descriptor data.
	 */
	dnew->next = descriptor_list;
	descriptor_list = dnew;

	/*
	 * Send the greeting.
	 */
	init_telnet (dnew);
	write_to_buffer (dnew,
					 "Would you like ANSI color? (Y)es, (N)o, (T)est:\n\r",
					 0);
	return;
}

void close_socket (DESCRIPTOR_DATA * dclose)
{
	CHAR_DATA *ch;

	if (dclose->outtop > 0)
		process_output (dclose, FALSE);

	if (dclose->snoop_by != NULL)
	{
		write_to_buffer (dclose->snoop_by,
						 "Your victim has left the game.\n\r", 0);
	}

	{
		DESCRIPTOR_DATA *d;

		for (d = descriptor_list; d != NULL; d = d->next)
		{
			if (d->snoop_by == dclose)
				d->snoop_by = NULL;
		}
	}

	if ((ch = dclose->character) != NULL)
	{
		sprintf (log_buf, "Closing link to %s.", ch->name);
		log_string (log_buf);
		/* cut down on wiznet spam when rebooting */
		/* If ch is writing note or playing, just lose link otherwise clear char */
		if ((dclose->connected == CON_PLAYING && !merc_down) ||
			((dclose->connected >= CON_NOTE_TO) &&
			 (dclose->connected <= CON_NOTE_FINISH)))
		{
			act ("$n has lost $s link.", ch, NULL, NULL, TO_ROOM);
			wiznet ("Net death has claimed $N.", ch, NULL, WIZ_LINKS, 0, 0);
			ch->desc = NULL;
		}
		else
		{
			free_char (dclose->original ? dclose->
					   original : dclose->character);
		}
	}

	free_runbuf (dclose);

	if (d_next == dclose)
		d_next = d_next->next;

	if (dclose == descriptor_list)
	{
		descriptor_list = descriptor_list->next;
	}
	else
	{
		DESCRIPTOR_DATA *d;

		for (d = descriptor_list; d && d->next != dclose; d = d->next)
			;
		if (d != NULL)
			d->next = dclose->next;
		else
			bug ("Close_socket: dclose not found.", 0);
	}

#if !defined(NO_MCCP)
	if (dclose->out_compress)
	{
		deflateEnd (dclose->out_compress);
		free_mem (dclose->out_compress_buf);
		free_mem (dclose->out_compress);
	}
#endif

	close (dclose->descriptor);
	free_descriptor (dclose);
	return;
}

bool read_from_descriptor (DESCRIPTOR_DATA * d)
{
	unsigned int iStart, index;
	static char buf[sizeof (d->inbuf)];

	memset (buf, 0, sizeof (buf));

	/* Hold horses if pending command already. */
	if (d->incomm[0] != '\0')
		return TRUE;

	/* Check for overflow. */
	iStart = strlen (d->inbuf);
	index = 0;

	if (iStart >= sizeof (d->inbuf) - 10)
	{
		logf ("%s input overflow!", d->host);
		write_to_descriptor (d, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0);
		d->inbuf[sizeof (d->inbuf) - 10] = '\0';
		return TRUE;
	}

	for (;;)
	{
		int nRead;

		nRead = read (d->descriptor, buf + index, sizeof (buf) - 10 - index);
		if (nRead > 0)
		{
			index += nRead;
			if (buf[index - 1] == '\n' || buf[index - 1] == '\r')
				break;
		}
		else if (nRead == 0)
		{
			logf ("Read_from_descriptor: EOF encountered on read.");
			return FALSE;
		}
		else if (WOULD_HAVE_BLOCKED)
			break;
		else
		{
			perror ("Read_from_descriptor");
			return FALSE;
		}
	}

	/* process_telnet does the actual writing in d->inbuf now! */
	process_telnet (d, index, buf);

	return TRUE;
}

/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer (DESCRIPTOR_DATA * d)
{
	int i, j, k;

	/*
	 * Hold horses if pending command already.
	 */
	if (d->incomm[0] != '\0')
		return;

	if (d->character && d->character->position == POS_FIGHTING && d->run_buf)
		free_runbuf (d);

	if (d->run_buf)
	{
		while (isdigit (*d->run_head) && *d->run_head != '\0')
		{
			char *s;
			char *e;

			s = (char *) d->run_head;
			while (isdigit (*s))
				s++;
			e = s;
			while (*(--s) == '0' && s != d->run_head);
			if (isdigit (*s) && *s != '0' && *e != 'o')
			{
				d->incomm[0] = *e;
				d->incomm[1] = '\0';
				s[0]--;
				while (isdigit (*(++s)))
					*s = '9';
				return;
			}
			if (*e == 'o')
				d->run_head = e;
			else
				d->run_head = ++e;
		}
		if (*d->run_head != '\0')
		{
			if (*d->run_head != 'o')
			{
				d->incomm[0] = *d->run_head++;
				d->incomm[1] = '\0';
				return;
			}
			else
			{
				char buf[MAX_INPUT_LENGTH];

				d->run_head++;

				strcpy (buf, "open ");
				switch (*d->run_head)
				{
				case 'n':
					strcat (buf, "north");
					break;
				case 's':
					strcat (buf, "south");
					break;
				case 'e':
					strcat (buf, "east");
					break;
				case 'w':
					strcat (buf, "west");
					break;
				case 'u':
					strcat (buf, "up");
					break;
				case 'd':
					strcat (buf, "down");
					break;
				default:
					return;
				}

				strcpy (d->incomm, buf);
				d->run_head++;
				return;
			}
		}
		free_runbuf (d);
	}

	/*
	 * Look for at least one new line.
	 */
	for (i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++)
	{
		if (d->inbuf[i] == '\0')
			return;
	}

	/*
	 * Canonical input processing.
	 */
	for (i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++)
	{
		if (k >= MAX_INPUT_LENGTH - 2)
		{
			write_to_descriptor (d, "Line too long.\n\r", 0);

			/* skip the rest of the line */
			for (; d->inbuf[i] != '\0'; i++)
			{
				if (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
					break;
			}
			d->inbuf[i] = '\n';
			d->inbuf[i + 1] = '\0';
			break;
		}

		if (d->inbuf[i] == '\b' && k > 0)
			--k;
		else if (isascii (d->inbuf[i]) && isprint (d->inbuf[i]))
			d->incomm[k++] = d->inbuf[i];
	}

	/*
	 * Finish off the line.
	 */
	if (k == 0)
		d->incomm[k++] = ' ';
	d->incomm[k] = '\0';

	/*
	 * Deal with bozos with #repeat 1000 ...
	 */

	if (k > 1 || d->incomm[0] == '!')
	{
		if (d->incomm[0] != '!' && strcmp (d->incomm, d->inlast))
		{
			d->repeat = 0;
		}
		else
		{
			if (++d->repeat >= 25 && d->character &&
				d->connected == CON_PLAYING)
			{
				sprintf (log_buf, "%s input spamming!", d->host);
				log_string (log_buf);
				wiznet
					("Spam spam spam $N spam spam spam spam spam!",
					 d->character, NULL, WIZ_SPAM, 0,
					 get_trust (d->character));
				if (d->incomm[0] == '!')
					wiznet (d->inlast, d->character, NULL,
							WIZ_SPAM, 0, get_trust (d->character));
				else
					wiznet (d->incomm, d->character, NULL,
							WIZ_SPAM, 0, get_trust (d->character));

				d->repeat = 0;
/*
		write_to_descriptor( d,
		    "\n\r*** PUT A LID ON IT!!! ***\n\r", 0 );
		strcpy( d->incomm, "quit" );
*/
			}
		}
	}

	/*
	 * Do '!' substitution.
	 */
	if (d->incomm[0] == '!')
		strcpy (d->incomm, d->inlast);
	else
		strcpy (d->inlast, d->incomm);

	/*
	 * Shift the input buffer.
	 */
	while (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
		i++;
	for (j = 0; (d->inbuf[j] = d->inbuf[i + j]) != '\0'; j++)
		;
	return;
}

char *random_colour (void)
{
	switch (number_range (1, 14))
	{
	case 1:
		return C_BLUE;
	case 2:
		return C_CYAN;
	case 3:
		return C_GREEN;
	case 4:
		return C_MAGENTA;
	case 5:
		return C_RED;
	case 6:
		return C_YELLOW;
	case 7:
		return CB_BLUE;
	case 8:
		return CB_CYAN;
	case 9:
		return CB_GREEN;
	case 10:
		return CB_MAGENTA;
	case 11:
		return CB_WHITE;
	case 12:
		return CB_RED;
	case 13:
		return C_WHITE;
	case 14:
		return CB_YELLOW;
	default:
		return C_CLEAR;
	}
}

char *random_background (void)
{
	switch (number_range (0, 13))
	{
	case 0:
		return BLACK_BG;
	case 1:
		return BLUE_BG;
	case 2:
		return CYAN_BG;
	case 3:
		return GREEN_BG;
	case 4:
		return MAGENTA_BG;
	case 5:
		return RED_BG;
	case 6:
		return YELLOW_BG;
	case 7:
		return BLUE_BBG;
	case 8:
		return CYAN_BBG;
	case 9:
		return GREEN_BBG;
	case 10:
		return MAGENTA_BBG;
	case 12:
		return RED_BBG;
	case 13:
		return YELLOW_BBG;
	default:
		return BLACK_BBG;
	}
}

/* Taken from Gary McNickel's WOTmud */
bool process_ansi_output (DESCRIPTOR_DATA * d)
{
	CHAR_DATA *ch;
	char *counter;
	char output[MSL];
	char temp[MSL];
	char *work;
	bool success = TRUE;
	char *i;

	if (d == NULL)
		return FALSE;

	ch = (d->original ? d->original : d->character);

	memset (output, 0, MSL);
	counter = output;
	work = d->outbuf;

	while (*work != '\0' && (work - d->outbuf) < d->outtop)
	{
		if ((int) (counter - output) >= MSL - 32)
		{

			*counter++ = '\0';

			if (!(success = write_to_descriptor (d, output, strlen (output))))
				break;

			memset (output, 0, MSL);
			counter = output;

		}

		if (*work == ANSI_KEY)
		{

			work++;

			temp[0] = '\0';

			if (DESC_FLAGGED (d, DESC_COLOUR) &&
				(!ch || !IS_SET (ch->comm, COMM_NOCOLOUR)))
			{
				switch (*work)
				{
				case '\0':
					break;
				case ' ':
					sprintf (temp, "%c ", ANSI_KEY);
					break;
				case 'x':
				case 'X':
					strcpy (temp, color_default (ch));
					break;
				case 'v':
				case 'V':
					strcpy (temp, C_REVERSE);
					break;
				case 'u':
				case 'U':
					strcpy (temp, C_UNDERSCORE);
					break;
				case 'f':
				case 'F':
					strcpy (temp, C_FLASH);
					break;
				case 'b':
					strcpy (temp, C_BLUE);
					break;
				case 'c':
					strcpy (temp, C_CYAN);
					break;
				case 'g':
					strcpy (temp, C_GREEN);
					break;
				case 'm':
					strcpy (temp, C_MAGENTA);
					break;
				case 'd':
					strcpy (temp, C_BLACK);
					break;
				case 'r':
					strcpy (temp, C_RED);
					break;
				case 'y':
					strcpy (temp, C_YELLOW);
					break;
				case 'w':
					strcpy (temp, C_WHITE);
					break;
				case 'B':
					strcpy (temp, CB_BLUE);
					break;
				case 'C':
					strcpy (temp, CB_CYAN);
					break;
				case 'G':
					strcpy (temp, CB_GREEN);
					break;
				case 'M':
					strcpy (temp, CB_MAGENTA);
					break;
				case 'D':
					strcpy (temp, CB_BLACK);
					break;
				case 'R':
					strcpy (temp, CB_RED);
					break;
				case 'W':
					strcpy (temp, CB_WHITE);
					break;
				case 'Y':
					strcpy (temp, CB_YELLOW);
					break;
				case '`':
					strcpy (temp, random_colour ());
					break;
				case '1':
					strcpy (temp, RED_BG);
					break;
				case '2':
					strcpy (temp, BLUE_BG);
					break;
				case '3':
					strcpy (temp, GREEN_BG);
					break;
				case '4':
					strcpy (temp, BLACK_BG);
					break;
				case '5':
					strcpy (temp, WHITE_BG);
					break;
				case '6':
					strcpy (temp, MAGENTA_BG);
					break;
				case '7':
					strcpy (temp, YELLOW_BG);
					break;
				case '8':
					strcpy (temp, CYAN_BG);
					break;
				case '!':
					strcpy (temp, RED_BBG);
					break;
				case '@':
					strcpy (temp, BLUE_BBG);
					break;
				case '#':
					strcpy (temp, GREEN_BBG);
					break;
				case '$':
					strcpy (temp, BLACK_BBG);
					break;
				case '%':
					strcpy (temp, WHITE_BBG);
					break;
				case '^':
					strcpy (temp, MAGENTA_BBG);
					break;
				case '&':
					strcpy (temp, YELLOW_BBG);
					break;
				case '*':
					strcpy (temp, CYAN_BBG);
					break;
				case '+':
				case '=':
					strcpy (temp, random_background ());
					break;
				case '-':
					sprintf (temp, "%c", C_TILDE);
					break;
				case 'P':
				case 'p':
					sprintf (temp, "%c", C_BEEP);
					break;
				case ANSI_KEY:
					sprintf (temp, "%c", ANSI_KEY);
					break;
				default:
					strcpy (temp, C_CLEAR);
					break;
				}
			}
			work++;

			i = temp;
			while ((*counter = *i) != '\0')
				++counter, ++i;
		}
		/* Custom colour added by Markanth */
		else if (*work == ANSI_CUSTOM)
		{
			int slot = 0;

			work++;

			while (*work != ANSI_END)
			{
				if (isdigit (*work))
					slot = (slot * 10) + (*work - '0');
				work++;
			}

			work++;

			temp[0] = '\0';

			if (slot < 0 || slot >= MAX_CUSTOM_COLOUR)
			{
				bug ("ansi_output: invalid custom color", 0);
				strcpy (temp, C_CLEAR);
			}
			else if (DESC_FLAGGED (d, DESC_COLOUR) &&
					 (!ch || !IS_SET (ch->comm, COMM_NOCOLOUR)))
			{
				strcpy (temp, char_colour (ch, slot));
			}

			i = temp;
			while ((*counter = *i) != '\0')
				++counter, ++i;
		}
		else
			*counter++ = *work++;
	}

	success = success && (write_to_descriptor (d, output, strlen (output)));

	d->outtop = 0;
	return success;

}

/*
 * Low level output function.
 */
bool process_output (DESCRIPTOR_DATA * d, bool fPrompt)
{
	/*
	 * Bust a prompt.
	 */
	if (!merc_down)
	{
		if (d->showstr_point)
			write_to_buffer (d, "[Hit Return to continue]\n\r", 0);
		else if (fPrompt && d->pString && d->connected == CON_PLAYING)
			write_to_buffer (d, "> ", 2);
		else if (fPrompt && d->connected == CON_NOTE_TEXT)
			write_to_buffer (d, "> ", 2);
		else if (fPrompt && d->connected == CON_PLAYING)
		{
			CHAR_DATA *ch;
			CHAR_DATA *victim;

			ch = d->character;

			/* battle prompt */
			if ((victim = ch->fighting) != NULL && can_see (ch, victim))
			{
				int percent;
				char wound[100];
				char buf[MAX_STRING_LENGTH];

				if (victim->max_hit > 0)
					percent = victim->hit * 100 / victim->max_hit;
				else
					percent = -1;

				if (percent >= 100)
					sprintf (wound, "is in excellent condition.");
				else if (percent >= 90)
					sprintf (wound, "has a few scratches.");
				else if (percent >= 75)
					sprintf (wound, "has some small wounds and bruises.");
				else if (percent >= 50)
					sprintf (wound, "has quite a few wounds.");
				else if (percent >= 30)
					sprintf (wound,
							 "has some big nasty wounds and scratches.");
				else if (percent >= 15)
					sprintf (wound, "looks pretty hurt.");
				else if (percent >= 0)
					sprintf (wound, "is in awful condition.");
				else
					sprintf (wound, "is bleeding to death.");

				sprintf (buf, "%s %s \n\r",
						 IS_NPC (victim) ? victim->short_descr :
						 victim->name, wound);
				buf[0] = UPPER (buf[0]);
				write_to_buffer (d, buf, 0);
			}

			ch = d->original ? d->original : d->character;
			if (!IS_SET (ch->comm, COMM_COMPACT))
				write_to_buffer (d, "\n\r", 2);

			if (IS_SET (ch->comm, COMM_PROMPT))
				bust_a_prompt (d->character);

			if (IS_SET (ch->comm, COMM_TELNET_GA))
			{
				write_to_buffer (d, go_ahead_str, 0);
			}
			else if (IS_SET (ch->comm, COMM_TELNET_EOR))
			{
				char eor_str[] = { IAC, EOR, '\0' };
				write_to_buffer (d, eor_str, 0);
			}
		}

	}

	/*
	 * Short-circuit if nothing to write.
	 */
	if (d->outtop == 0)
		return TRUE;

	/*
	 * Snoop-o-rama.
	 */
	if (d->snoop_by != NULL)
	{
		if (d->character != NULL)
			write_to_buffer (d->snoop_by, d->character->name, 0);
		write_to_buffer (d->snoop_by, "> ", 2);
		write_to_buffer (d->snoop_by, d->outbuf, d->outtop);
	}

	/*
	 * OS-dependent output.
	 */
	return process_ansi_output (d);
}

/*
 * Bust a prompt (player settable prompt)
 * coded by Morgenes for Aldara Mud
 */
void bust_a_prompt (CHAR_DATA * ch)
{
	char buf[MAX_STRING_LENGTH];
	char buf2[MAX_STRING_LENGTH];
	const char *str;
	const char *i;
	char *point;
	char doors[MAX_INPUT_LENGTH];
	EXIT_DATA *pexit;
	bool found;
	const char *dir_letter[] = { "N", "E", "S", "W", "U", "D" };
	int door;

	point = buf;
	str = ch->prompt;
	if (str == NULL || str[0] == '\0')
	{
		chprintlnf (ch, "<%ldhp %ldm %ldmv> %s", ch->hit, ch->mana,
					ch->move, ch->prefix);
		return;
	}

	if (IS_SET (ch->comm, COMM_AFK))
	{
		chprint (ch, "<AFK> ");
		return;
	}

	while (*str != '\0')
	{
		if (*str != '%')
		{
			*point++ = *str++;
			continue;
		}
		++str;
		switch (*str)
		{
		default:
			i = " ";
			break;
		case 'e':
			found = FALSE;
			doors[0] = '\0';
			for (door = 0; door < 6; door++)
			{
				if ((pexit = ch->in_room->exit[door]) != NULL &&
					pexit->u1.to_room != NULL &&
					(can_see_room (ch, pexit->u1.to_room) ||
					 (IS_AFFECTED (ch, AFF_INFRARED) &&
					  !IS_AFFECTED (ch, AFF_BLIND))) &&
					!IS_SET (pexit->exit_info, EX_CLOSED))
				{
					found = TRUE;
					strcat (doors, dir_letter[door]);
				}
			}
			if (!found)
				strcat (buf, "none");
			sprintf (buf2, "%s", doors);
			i = buf2;
			break;
		case 'c':
			sprintf (buf2, "%s", "\n\r");
			i = buf2;
			break;
		case 'h':
			sprintf (buf2, "%ld", ch->hit);
			i = buf2;
			break;
		case 'H':
			sprintf (buf2, "%ld", ch->max_hit);
			i = buf2;
			break;
		case 'm':
			sprintf (buf2, "%ld", ch->mana);
			i = buf2;
			break;
		case 'M':
			sprintf (buf2, "%ld", ch->max_mana);
			i = buf2;
			break;
		case 'v':
			sprintf (buf2, "%ld", ch->move);
			i = buf2;
			break;
		case 'V':
			sprintf (buf2, "%ld", ch->max_move);
			i = buf2;
			break;
		case 'x':
			sprintf (buf2, "%d", ch->exp);
			i = buf2;
			break;
		case 'X':
			sprintf (buf2, "%d",
					 IS_NPC (ch) ? 0 : (ch->level +
										1) * exp_per_level (ch,
															ch->
															pcdata->points) -
					 ch->exp);
			i = buf2;
			break;
		case 'g':
			sprintf (buf2, "%ld", ch->gold);
			i = buf2;
			break;
		case 's':
			sprintf (buf2, "%ld", ch->silver);
			i = buf2;
			break;
		case 'a':
			if (ch->level > 9)
				sprintf (buf2, "%d", ch->alignment);
			else
				sprintf (buf2, "%s",
						 IS_GOOD (ch) ? "good" : IS_EVIL (ch) ?
						 "evil" : "neutral");
			i = buf2;
			break;
		case 'r':
			if (ch->in_room != NULL)
				sprintf (buf2, "%s",
						 ((!IS_NPC (ch) &&
						   IS_SET (ch->act, PLR_HOLYLIGHT)) ||
						  (!IS_AFFECTED (ch, AFF_BLIND) &&
						   !room_is_dark (ch->in_room))) ? ch->in_room->
						 name : "darkness");
			else
				sprintf (buf2, " ");
			i = buf2;
			break;
		case 'R':
			if (IS_IMMORTAL (ch) && ch->in_room != NULL)
				sprintf (buf2, "%ld", ch->in_room->vnum);
			else
				sprintf (buf2, " ");
			i = buf2;
			break;
		case 'Q':
			if (ON_GQUEST (ch))
				sprintf (buf2, "%d", gquest_info.timer);
			else
				sprintf (buf2, "%dn", gquest_info.next);
			i = buf2;
			break;

		case 'z':
			if (IS_IMMORTAL (ch) && ch->in_room != NULL)
				sprintf (buf2, "%s", ch->in_room->area->name);
			else
				sprintf (buf2, " ");
			i = buf2;
			break;
		case '%':
			sprintf (buf2, "%%");
			i = buf2;
			break;
		case 'o':
			sprintf (buf2, "%s", olc_ed_name (ch));
			i = buf2;
			break;
		case 'O':
			sprintf (buf2, "%s", olc_ed_vnum (ch));
			i = buf2;
			break;
		case 'q':
			if (!IS_NPC (ch))
			{
				if (!IS_SET (ch->act, PLR_QUESTOR))
					sprintf (buf2, "%d", ch->pcdata->nextquest);
				else
					sprintf (buf2, "%d", ch->pcdata->countdown);
			}
			else
				strcpy (buf2, " ");

			i = buf2;
			break;
		}
		++str;
		while ((*point = *i) != '\0')
			++point, ++i;
	}
	write_to_buffer (ch->desc, buf, point - buf);

	if (ch->prefix[0] != '\0')
		write_to_buffer (ch->desc, ch->prefix, 0);
	return;
}

/*
 * Append onto an output buffer.
 */
void write_to_buffer (DESCRIPTOR_DATA * d, const char *txt, int length)
{
	/*
	 * Find length in case caller didn't.
	 */
	if (length <= 0)
		length = strlen (txt);

	/*
	 * Initial \n\r if needed.
	 */
	if (d->outtop == 0 && !d->fcommand)
	{
		d->outbuf[0] = '\n';
		d->outbuf[1] = '\r';
		d->outtop = 2;
	}

	/*
	 * Expand the buffer as needed.
	 */
	while (d->outtop + length >= d->outsize)
	{
		char *outbuf;

		if (d->outsize >= 32000)
		{
			bug ("Buffer overflow. Closing.\n\r", 0);
			close_socket (d);
			return;
		}
		alloc_mem (outbuf, char, 2 * d->outsize);
		strncpy (outbuf, d->outbuf, d->outtop);
		free_mem (d->outbuf);
		d->outbuf = outbuf;
		d->outsize *= 2;
	}

	/*
	 * Copy.
	 */
	strncpy ((char *) d->outbuf + d->outtop, txt, length);
	d->outtop += length;
	return;
}

/*
 * Lowest level output function.
 * Write a block of text to the file descriptor.
 * If this gives errors on very long blocks (like 'ofind all'),
 *   try lowering the max block size.
 */
bool write_to_descriptor (DESCRIPTOR_DATA * d, char *txt, int length)
{
	int iStart;
	int nWrite;
	int nBlock;

	if (length <= 0)
		length = strlen (txt);

#if !defined(NO_MCCP)
	if (d->out_compress)
		return writeCompressed (d, txt, length);
	else
#endif
	{
		for (iStart = 0; iStart < length; iStart += nWrite)
		{
			nBlock = UMIN (length - iStart, 4096);
			if ((nWrite = write (d->descriptor, txt + iStart, nBlock)) < 0)
			{
				perror ("Write_to_descriptor");
				return FALSE;
			}
		}

		return TRUE;
	}
}


/*
 * Parse a name for acceptability.
 */
bool check_parse_name (const char *name)
{
	int clan;

	/*
	 * Reserved words.
	 */
	if (is_exact_name
		(name, "all auto immortal self someone something the you loner none"))
	{
		return FALSE;
	}

	/* check clans */
	for (clan = 0; clan < maxClan; clan++)
	{
		if (LOWER (name[0]) == LOWER (clan_table[clan].name[0]) &&
			!str_cmp (name, clan_table[clan].name))
			return FALSE;
	}

	if (str_cmp (capitalize (name), "Alander") &&
		(!str_prefix ("Alan", name) || !str_suffix ("Alander", name)))
		return FALSE;

	/*
	 * Length restrictions.
	 */

	if (strlen (name) < 2)
		return FALSE;

	if (strlen (name) > 12)
		return FALSE;

	/*
	 * Alphanumerics only.
	 * Lock out IllIll twits.
	 */
	{
		const char *pc;
		bool fIll, adjcaps = FALSE, cleancaps = FALSE;
		unsigned int total_caps = 0;

		fIll = TRUE;
		for (pc = name; *pc != '\0'; pc++)
		{
			if (!isalpha (*pc))
				return FALSE;

			if (isupper (*pc))	/* ugly anti-caps hack */
			{
				if (adjcaps)
					cleancaps = TRUE;
				total_caps++;
				adjcaps = TRUE;
			}
			else
				adjcaps = FALSE;

			if (LOWER (*pc) != 'i' && LOWER (*pc) != 'l')
				fIll = FALSE;
		}

		if (fIll)
			return FALSE;

		if (cleancaps ||
			(total_caps > (strlen (name)) / 2 && strlen (name) < 3))
			return FALSE;
	}

	/*
	 * Prevent players from naming themselves after mobs.
	 */
	{
		MOB_INDEX_DATA *pMobIndex;
		int iHash;

		for (iHash = 0; iHash < MAX_KEY_HASH; iHash++)
		{
			for (pMobIndex = mob_index_hash[iHash]; pMobIndex != NULL;
				 pMobIndex = pMobIndex->next)
			{
				if (is_name (name, pMobIndex->player_name))
					return FALSE;
			}
		}
	}

	return TRUE;
}

/*
 * Look for link-dead player to reconnect.
 */
bool check_reconnect (DESCRIPTOR_DATA * d, const char *name, bool fConn)
{
	CHAR_DATA *ch;

	for (ch = char_list; ch != NULL; ch = ch->next)
	{
		if (!IS_NPC (ch) && (!fConn || ch->desc == NULL) &&
			!str_cmp (d->character->name, ch->name))
		{
			if (fConn == FALSE)
			{
				free_string (d->character->pcdata->pwd);
				d->character->pcdata->pwd = str_dup (ch->pcdata->pwd);
			}
			else
			{
				free_char (d->character);
				d->character = ch;
				ch->desc = d;
				ch->timer = 0;
				chprintln (ch,
						   "Reconnecting. Type replay to see missed tells.");
				act ("$n has reconnected.", ch, NULL, NULL, TO_ROOM);

				sprintf (log_buf, "%s@%s reconnected.", ch->name, d->host);
				log_string (log_buf);
				wiznet ("$N groks the fullness of $S link.", ch,
						NULL, WIZ_LINKS, 0, 0);
				d->connected = CON_PLAYING;
				/* Inform the character of a note in progress and the possbility
				 * of continuation!
				 */
				if (ch->pcdata->in_progress)
					chprintln (ch,
							   "You have a note in progress. Type NOTE WRITE to continue it.");
			}
			return TRUE;
		}
	}

	return FALSE;
}

/*
 * Check if already playing.
 */
bool check_playing (DESCRIPTOR_DATA * d, const char *name)
{
	DESCRIPTOR_DATA *dold;

	for (dold = descriptor_list; dold; dold = dold->next)
	{
		if (dold != d && dold->character != NULL &&
			dold->connected != CON_GET_NAME &&
			dold->connected != CON_GET_OLD_PASSWORD &&
			!str_cmp (name,
					  dold->original ? dold->original->
					  name : dold->character->name))
		{
			write_to_buffer (d, "That character is already playing.\n\r", 0);
			write_to_buffer (d, "Do you wish to connect anyway (Y/N)?", 0);
			d->connected = CON_BREAK_CONNECT;
			return TRUE;
		}
	}

	return FALSE;
}

void stop_idling (CHAR_DATA * ch)
{
	if (ch == NULL || ch->desc == NULL || ch->desc->connected != CON_PLAYING
		|| ch->was_in_room == NULL ||
		ch->in_room != get_room_index (ROOM_VNUM_LIMBO))
		return;

	ch->timer = 0;
	char_from_room (ch);
	char_to_room (ch, ch->was_in_room);
	ch->was_in_room = NULL;
	act ("$n has returned from the void.", ch, NULL, NULL, TO_ROOM);
	return;
}

/*
 * Write to one char.
 */
void chprint (CHAR_DATA * ch, const char *txt)
{
	if (txt != NULL && ch && ch->desc != NULL)
		write_to_buffer (ch->desc, txt, strlen (txt));
	return;
}

void chprintln (CHAR_DATA * ch, const char *txt)
{
	if (ch && ch->desc != NULL)
	{
		if (txt != NULL)
			write_to_buffer (ch->desc, txt, strlen (txt));
		write_to_buffer (ch->desc, "\n\r", 2);
	}

	return;
}

/*
 * Send a page to one char.
 */
void page_to_char (const char *txt, CHAR_DATA * ch)
{
	if (txt == NULL || ch->desc == NULL)
		return;

	if (ch->lines == 0)
	{
		chprint (ch, txt);
		return;
	}

	alloc_mem (ch->desc->showstr_head, char, strlen (txt) + 1);
	strcpy ((char *) ch->desc->showstr_head, txt);
	ch->desc->showstr_point = ch->desc->showstr_head;
	show_string (ch->desc, "");
}

/* string pager */
void show_string (struct descriptor_data *d, char *input)
{
	char buffer[4 * MAX_STRING_LENGTH];
	char buf[MAX_INPUT_LENGTH];
	register const char *chk;
	register char *scan;
	int lines = 0, toggle = 1;
	int show_lines;

	one_argument (input, buf);
	if (buf[0] != '\0')
	{
		if (d->showstr_head)
		{
			free_mem (d->showstr_head);
			d->showstr_head = 0;
		}
		d->showstr_point = 0;
		return;
	}

	if (d->character)
		show_lines = d->character->lines;
	else
		show_lines = 0;

	for (scan = buffer;; scan++, d->showstr_point++)
	{
		if (((*scan = *d->showstr_point) == '\n' || *scan == '\r') &&
			(toggle = -toggle) < 0)
			lines++;

		else if (!*scan || (show_lines > 0 && lines >= show_lines))
		{
			*scan = '\0';
			write_to_buffer (d, buffer, strlen (buffer));
			for (chk = d->showstr_point; isspace (*chk); chk++);
			{
				if (!*chk)
				{
					if (d->showstr_head)
					{
						free_mem (d->showstr_head);
						d->showstr_head = 0;
					}
					d->showstr_point = 0;
				}
			}
			return;
		}
	}
	return;
}

/* quick sex fixer */
void fix_sex (CHAR_DATA * ch)
{
	if (ch->sex < 0 || ch->sex > 2)
		ch->sex = IS_NPC (ch) ? 0 : ch->pcdata->true_sex;
}

void act_new (const char *format, CHAR_DATA * ch, const void *arg1,
			  const void *arg2, int type, int min_pos)
{
	static char *const he_she[] = { "it", "he", "she" };
	static char *const him_her[] = { "it", "him", "her" };
	static char *const his_her[] = { "its", "his", "her" };

	char buf[MAX_STRING_LENGTH];
	char fname[MAX_INPUT_LENGTH];
	CHAR_DATA *to;
	CHAR_DATA *vch = (CHAR_DATA *) arg2;
	OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
	OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
	const char *str;
	const char *i;
	char *point;

	/*
	 * Discard null and zero-length messages.
	 */
	if (format == NULL || format[0] == '\0')
		return;

	/* discard null rooms and chars */
	if (ch == NULL || ch->in_room == NULL)
		return;

	to = ch->in_room->people;
	if (type == TO_VICT)
	{
		if (vch == NULL)
		{
			bug ("Act: null vch with TO_VICT.", 0);
			return;
		}

		if (vch->in_room == NULL)
			return;

		to = vch->in_room->people;
	}

	for (; to != NULL; to = to->next_in_room)
	{
		if ((!IS_NPC (to) && to->desc == NULL) ||
			(IS_NPC (to) && to->desc == NULL
			 && !HAS_TRIGGER (to, TRIG_ACT)) || to->position < min_pos)
			continue;

		if ((type == TO_CHAR) && to != ch)
			continue;
		if (type == TO_VICT && (to != vch || to == ch))
			continue;
		if (type == TO_ROOM && to == ch)
			continue;
		if (type == TO_NOTVICT && (to == ch || to == vch))
			continue;

		point = buf;
		str = format;
		while (*str != '\0')
		{
			if (*str != '$')
			{
				*point++ = *str++;
				continue;
			}
			++str;

			if (arg2 == NULL && *str >= 'A' && *str <= 'Z')
			{
				bug ("Act: missing arg2 for code %d.", *str);
				i = " <@@@> ";
			}
			else
			{
				switch (*str)
				{
				default:
					bug ("Act: bad code %d.", *str);
					i = " <@@@> ";
					break;
					/* Thx alex for 't' idea */
				case 't':
					i = (char *) arg1;
					break;
				case 'T':
					i = (char *) arg2;
					break;
				case 'n':
					i = PERS (ch, to);
					break;
				case 'N':
					i = PERS (vch, to);
					break;
				case 'e':
					i = he_she[URANGE (0, ch->sex, 2)];
					break;
				case 'E':
					i = he_she[URANGE (0, vch->sex, 2)];
					break;
				case 'm':
					i = him_her[URANGE (0, ch->sex, 2)];
					break;
				case 'M':
					i = him_her[URANGE (0, vch->sex, 2)];
					break;
				case 's':
					i = his_her[URANGE (0, ch->sex, 2)];
					break;
				case 'S':
					i = his_her[URANGE (0, vch->sex, 2)];
					break;

				case 'p':
					i = can_see_obj (to,
									 obj1) ? obj1->short_descr : "something";
					break;

				case 'P':
					i = can_see_obj (to,
									 obj2) ? obj2->short_descr : "something";
					break;

				case 'd':
					if (arg2 == NULL || ((char *) arg2)[0] == '\0')
					{
						i = "door";
					}
					else
					{
						one_argument ((char *) arg2, fname);
						i = fname;
					}
					break;
				}
			}

			++str;
			while ((*point = *i) != '\0')
				++point, ++i;
		}

		*point++ = '\n';
		*point++ = '\r';
		*point = '\0';
		buf[0] = UPPER (buf[0]);
		if (to->desc != NULL)
		{
			if (to->desc->connected == CON_PLAYING)
				write_to_buffer (to->desc, buf, point - buf);
		}
		else if (IS_NPC (to) && MOBtrigger)
			mp_act_trigger (buf, to, ch, arg1, arg2, TRIG_ACT);
	}
	return;
}

/* source: EOD, by John Booth <???> */

void chprintf (CHAR_DATA * ch, char *fmt, ...)
{
	char buf[MAX_STRING_LENGTH];
	va_list args;
	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	chprint (ch, buf);
}

void chprintlnf (CHAR_DATA * ch, char *fmt, ...)
{
	char buf[MAX_STRING_LENGTH];
	va_list args;
	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	chprintln (ch, buf);
}
void bugf (char *fmt, ...)
{
	char buf[2 * MSL];
	va_list args;
	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	bug (buf, 0);
}

void logf (char *fmt, ...)
{
	char buf[2 * MSL];
	va_list args;
	va_start (args, fmt);
	vsprintf (buf, fmt, args);
	va_end (args);

	log_string (buf);
}

#if !defined(WIN32)

#define	CORE_EXAMINE_SCRIPT	"../corefiles/gdbscript"

void halt_mud (int sig)
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *ch;
	extern char last_command2[MAX_INPUT_LENGTH];
	extern DESCRIPTOR_DATA *last_descriptor;
	struct sigaction default_action;
	int i;
	pid_t forkpid;

	wait (NULL);
	if (!crashed)
	{
		crashed++;
		fprintf (stderr, "GAME CRASHED (SIGNAL %d).\rLast command: %s\r",
				 sig, last_command);
		// Inform last command typer that he caused the crash
		if (strlen (last_command2))
		{
			write_to_descriptor (last_descriptor,
								 "\n\rThe last command you typed, '", 0);
			write_to_descriptor (last_descriptor, last_command2, 0);
			write_to_descriptor (last_descriptor,
								 "', might have caused this crash.\n\r"
								 "Please note any unusual circumstances to IMP and avoid using that command.\n\r",
								 0);
		}
		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;
			ch = CH (d);
			if (!ch)
			{
				close_socket (d);
				continue;
			}
			if (IS_NPC (ch))
				continue;
			write_to_descriptor (d, "\n\rThe mud has CRASHED.\007\n\r", 0);
		}

		// try to save all characters - save_char_obj has sanity checking
		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;
			ch = CH (d);
			if (!ch)
			{
				close_socket (d);
				continue;
			}
			save_char_obj (ch);
		}

		// success - proceed with fork/copyover plan.  Otherwise will go to
		// next section and crash with a full reboot to recover
		if ((forkpid = fork ()) > 0)
		{
			// Parent process copyover and exit 
			waitpid (forkpid, NULL, WNOHANG | WUNTRACED);
			// this requires you to add an "if (ch)" before the chprint
			// statements in do_copyover.
			do_copyover (NULL, "");
			exit (0);
		}
		else if (forkpid < 0)
		{
			exit (1);
		}
		// Child process proceed to dump
		// Close all files!
		for (i = 255; i >= 0; i--)
			close (i);

		// Dup /dev/null to STD{IN,OUT,ERR}
		open ("/dev/null", O_RDWR);
		dup (0);
		dup (0);

		default_action.sa_handler = SIG_DFL;
		sigaction (sig, &default_action, NULL);

		// I run different scripts depending on my port
		if (!fork ())
		{
			execl (CORE_EXAMINE_SCRIPT, CORE_EXAMINE_SCRIPT, (char *) NULL);
			exit (0);
		}
		else
			return;
		raise (sig);
	}

	if (crashed == 1)
	{
		crashed++;

		for (d = descriptor_list; d != NULL; d = d_next)
		{
			d_next = d->next;
			ch = d->original ? d->original : d->character;
			if (ch == NULL)
			{
				close_socket (d);
				continue;
			}
			if (IS_NPC (ch))
				continue;
			write_to_descriptor (d,
								 "** Error saving character files; conducting full reboot. **\007\n\r",
								 0);
			close_socket (d);
			continue;
		}
		fprintf (stderr, "CHARACTERS NOT SAVED.\r");
		default_action.sa_handler = SIG_DFL;
		sigaction (sig, &default_action, NULL);

		if (!fork ())
		{
			kill (getppid (), sig);
			exit (1);
		}
		else
			return;
		raise (sig);
	}

	if (crashed == 2)
	{
		crashed++;
		fprintf (stderr, "TOTAL GAME CRASH.");
		default_action.sa_handler = SIG_DFL;
		sigaction (sig, &default_action, NULL);

		if (!fork ())
		{
			kill (getppid (), sig);
			exit (1);
		}
		else
			return;
		raise (sig);
	}

	if (crashed == 3)
	{
		default_action.sa_handler = SIG_DFL;
		sigaction (sig, &default_action, NULL);

		if (!fork ())
		{
			kill (getppid (), sig);
			exit (1);
		}
		else
			return;
		raise (sig);
	}
}
#endif