1stMUD/corefiles/
1stMUD/gods/
1stMUD/log/
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>
#define __USE_GNU				/* for strsignal() */
#define _GNU_SOURCE
#include <string.h>
#undef __USE_GNU
#undef _GNU_SOURCE
#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
#include "webserver.h"

#include <signal.h>

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

/*
 * Global variables.
 */
DESCRIPTOR_DATA *d_next;		/* Next descriptor in loop  */
bool god;						/* All new chars are gods!  */
bool merc_down;					/* Shutdown         */
char str_boot_time[MAX_INPUT_LENGTH];
/* Needs to be global because of do_copyover */
int port, control;
/* Global variable */
#if !defined(WIN32) && !defined(__CYGWIN__)
volatile sig_atomic_t crashed = 0;	/* Are we currently crashing? */
struct itimerval vtimer;
#endif

#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));
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));
void set_game_levels args((int Old, int New));
void save_helps args((void));

void set_vtimer(long sec)
{
#if !defined(WIN32) && !defined(__CYGWIN__)
#define DEFAULT_VTIMER  (60 * 60 * 3)	// 3 minutes
	vtimer.it_value.tv_sec = sec == -1 ? DEFAULT_VTIMER : sec;
	vtimer.it_value.tv_usec = 0;

	if (setitimer(ITIMER_VIRTUAL, &vtimer, NULL) < 0)
	{
		log_string("Failed to set vtimer.");
		exit(1);
	}
#endif
}

#if !defined(WIN32)
#if !defined(__CYGWIN__)

void sigvalarm(int sig)
{
	static int safe_check = 0;
	char crash_message_a[] =
		"The mud has been looping for the past 60 seconds.";
	char crash_message_b[] = "Initiating reboot...";
	char crash_message_c[] =
		"The mud failed to inform the players of the above.";

	switch (safe_check)
	{
	case 0:
		safe_check = 1;
		bug(crash_message_a, 0);
		bug(crash_message_b, 0);
		break;

	case 1:
		safe_check = 2;
		log_string(crash_message_a);
		log_string(crash_message_b);
		log_string(crash_message_c);
		break;

	case 2:
		break;
	}

	/* Reboot the MUD */

	set_vtimer(-1);

	halt_mud(sig);				// Shouldn't return

	exit(1);					// Last resort
}

// Stop an endless loop 
static void sigalrm(int sig)
{
	static int attempt = 0;
	time_t ptm;

	time(&ptm);
	log_string("TOCK!");

	if ((ptm - current_time) > 200 || crashed)
	{
		if (attempt != 1)
		{
			attempt = 1;		// Try to reboot once...

			halt_mud(sig);		// Should NOT return
		}

		raise(SIGSEGV);			// Something's wrong, cause a crash
		exit(0);
	}
}

struct sigaction halt_action, ignore_action, alarm_action, valarm_action;

struct signal_type
{
	int signum;
	struct sigaction *act;
	const char *name;			// not used, strsignal() prefered.
	const char *desc;			// not used, strsignal() prefered.
};

const struct signal_type signal_table[] = {

	{SIGPIPE, &ignore_action, "SIGPIPE",
	 "Broken pipe: write to pipe with no readers"},
	{SIGCHLD, &ignore_action, "SIGCHLD", "Child stopped or terminated"},

	{SIGHUP, &ignore_action, "SIGHUP",
	 "Hangup detected on controlling terminal or death of controlling process"},
	{SIGINT, &halt_action, "SIGINT", "Interrupt from keyboard"},
	{SIGQUIT, &halt_action, "SIGQUIT", "Quit from keyboard"},
	{SIGILL, &halt_action, "SIGILL", "Illegal Instruction"},
	{SIGFPE, &halt_action, "SIGFPE", "Floating point exception"},
	{SIGSEGV, &halt_action, "SIGSEGV", "Invalid memory reference"},
	{SIGTERM, &halt_action, "SIGTERM", "Termination signal"},
	{SIGBUS, &halt_action, "SIGBUS", "Bus error (bad memory access)"},
	{SIGALRM, &alarm_action, "SIGALRM", "Timer signal from alarm(2)"},
	{SIGVTALRM, &valarm_action, "SIGVTALRM", "Virtual alarm clock"},
	{-1, NULL, NULL, NULL}
};

void set_signals(void)
{
	int i;

	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;

	valarm_action.sa_handler = sigvalarm;
	sigemptyset(&valarm_action.sa_mask);
	valarm_action.sa_flags = SA_NODEFER;

	for (i = 0; signal_table[i].signum != -1; i++)
		sigaction(signal_table[i].signum, signal_table[i].act, NULL);

	vtimer.it_interval.tv_sec = 60;
	vtimer.it_interval.tv_usec = 0;
	set_vtimer(-1);
}

#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;
#if !defined(WIN32) && !defined(__CYGWIN__)
	bool fCopyOver = FALSE;

	set_signals();
#endif

	/*
	 * Init time.
	 */
	tzset();
	gettimeofday(&now_time, NULL);
	current_time = (time_t) now_time.tv_sec;
	strcpy(str_boot_time, str_time(current_time, -1, NULL));

	/*
	 * 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 (!str_cmp(argv[1], "levels"))
		{
			if (IS_NULLSTR(argv[2]) || IS_NULLSTR(argv[3])
				|| !is_number(argv[2]) || !is_number(argv[3]))
			{
				logf("Usage: %s levels [old max level] [new max level]",
					 argv[0]);
				exit(1);
			}

			boot_db();
			set_game_levels(atoi(argv[2]), atoi(argv[3]));
			exit(0);
		}
		else if (!is_number(argv[1]))
		{
			logf("Usage: %s [port #]\n", argv[0]);
			exit(1);
		}
		else if ((port = atoi(argv[1])) <= 1024)
		{
			logf("Port number must be above 1024.\n");
			exit(1);
		}
#if !defined(WIN32) && !defined(__CYGWIN__)
		/* Are we recovering from a copyover? */
		if (argv[2] && argv[2][0])
		{
			fCopyOver = TRUE;
			control = atoi(argv[3]);
		}
		else
			fCopyOver = FALSE;
#endif
	}

#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)
		{
			logf("Error %i on WSAStartup\n", err);
			exit(1);
		}
	}
#endif

	/*
	 * Run the game.
	 */
#if !defined(WIN32) && !defined(__CYGWIN__)
	if (!fCopyOver)
#endif
		control = init_socket(port);
#if !defined(NO_WEB)
	WebUP = init_web_server();
#endif
	boot_db();
	sprintf(log_buf, "ROM is ready to rock on port %d.", port);
	log_string(log_buf);
#if !defined(WIN32) && !defined(__CYGWIN__)
	if (fCopyOver)
		copyover_recover();
#endif
	game_loop(control);
#if !defined(NO_WEB)
	shutdown_web_server();
#endif
	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 = { 0, 0 };
	int vt_set = 0;

#if !defined(WIN32)
	signal(SIGPIPE, SIG_IGN);
	signal(SIGCHLD, SIG_IGN);
#endif
	SynchronizeClock();

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

		/*
		 * 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_first; 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_first; 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_first; 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);

				vt_set = 0;
				set_vtimer(60);

				/* 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:
						substitute_alias(d, d->incomm);
						break;
					default:
						nanny(d, d->incomm);
						break;
					}

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

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

#if !defined(NO_WEB)
		if (WebUP)
			update_web_server();
#endif

		/*
		 * Output.
		 */
		for (d = descriptor_first; 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();
		if (++vt_set >= 35)
		{
			vt_set = 0;

			set_vtimer(60);
		}
	}

	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.
	 */
	LINK(dnew, descriptor_first, descriptor_last, next, prev);

	/*
	 * 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_first; 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;

	UNLINK(dclose, descriptor_first, descriptor_last, next, prev);

#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);

	if (d->connected == CON_GET_TERM && (DESC_FLAGGED(d, DESC_TELOPT_EOR) ||
										 DESC_FLAGGED(d, DESC_TELOPT_ECHO)
										 || DESC_FLAGGED(d, DESC_TELOPT_NAWS)
#if !defined(NO_MCCP)
										 || d->out_compress
#endif
		))
	{
		SET_BIT(d->d_flags, DESC_COLOUR);
		write_to_buffer(d, "{`Colour{c enabled Automatically...{x\n\r", 0);
		show_greeting(d);
		d->connected = CON_GET_NAME;
	}
	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] != '!' && str_cmp(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;
}

int col_attr, col_fore, col_back;

int random_attr(void)
{
	if (number_range(1, 50) < 25)
		return CL_CLEAR;
	else
		return CL_BRIGHT;
}

int random_fore(void)
{
	return number_range(FG_BLACK, FG_WHITE);
}

int random_back(void)
{
	return number_range(BG_BLACK, BG_WHITE);
}

char *make_colour(void)
{
	bool a, b;

	if (col_attr == CL_RANDOM)
		col_attr = random_attr();
	if (col_fore == FG_RANDOM)
		col_fore = random_fore();
	if (col_back == BG_RANDOM)
		col_back = random_back();

	a = (col_fore != FG_NONE);
	b = (col_back != BG_NONE);

	if (!a && !b)
		return FORMATF(CL_FORMAT1, col_attr);
	else if (a && !b)
		return FORMATF(CL_FORMAT2, col_attr, col_fore);
	else if (!a && b)
		return FORMATF(CL_FORMAT2, col_attr, col_back);
	else
		return FORMATF(CL_FORMAT3, col_attr, col_fore, col_back);
}

/* Taken from Gary McNickel's WOTmud 
   Updated by Markanth */
bool process_ansi_output(DESCRIPTOR_DATA * d)
{
	CHAR_DATA *ch;
	char *counter;
	char output[MSL * 10];
	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, sizeof(output));
	counter = output;
	work = d->outbuf;

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

			*counter++ = '\0';

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

			memset(output, 0, sizeof(output));
			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 '-':
					strcpy(temp, "~");
					break;
				case 'P':
				case 'p':
					sprintf(temp, "%c", 007);
					break;
				case ANSI_KEY:
					sprintf(temp, "%c", ANSI_KEY);
					break;
				default:
					{
						switch (*work)
						{
						case 'x':
						case 'X':
							strcpy(temp, char_colour(ch, _DEFAULT));
							break;
						case 'v':
						case 'V':
							col_attr = CL_REVERSE;
							break;
						case 'u':
						case 'U':
							col_attr = CL_UNDER;
							break;
						case 'f':
						case 'F':
							col_attr = CL_BLINK;
							break;
						case 'b':
							col_attr = CL_CLEAR;
							col_fore = FG_BLUE;
							break;
						case 'c':
							col_attr = CL_CLEAR;
							col_fore = FG_CYAN;
							break;
						case 'g':
							col_attr = CL_CLEAR;
							col_fore = FG_GREEN;
							break;
						case 'm':
							col_attr = CL_CLEAR;
							col_fore = FG_MAGENTA;
							break;
						case 'd':
							col_attr = CL_CLEAR;
							col_fore = FG_BLACK;
							break;
						case 'r':
							col_attr = CL_CLEAR;
							col_fore = FG_RED;
							break;
						case 'y':
							col_attr = CL_CLEAR;
							col_fore = FG_YELLOW;
							break;
						case 'w':
							col_attr = CL_CLEAR;
							col_fore = FG_WHITE;
							break;
						case 'B':
							col_attr = CL_BRIGHT;
							col_fore = FG_BLUE;
							break;
						case 'C':
							col_attr = CL_BRIGHT;
							col_fore = FG_CYAN;
							break;
						case 'G':
							col_attr = CL_BRIGHT;
							col_fore = FG_GREEN;
							break;
						case 'M':
							col_attr = CL_BRIGHT;
							col_fore = FG_MAGENTA;
							break;
						case 'D':
							col_attr = CL_BRIGHT;
							col_fore = FG_BLACK;
							break;
						case 'R':
							col_attr = CL_BRIGHT;
							col_fore = FG_RED;
							break;
						case 'W':
							col_attr = CL_BRIGHT;
							col_fore = FG_WHITE;
							break;
						case 'Y':
							col_attr = CL_BRIGHT;
							col_fore = FG_YELLOW;
							break;
						case '`':
							col_attr = CL_RANDOM;
							col_fore = FG_RANDOM;
							break;
						case '1':
							col_back = BG_RED;
							break;
						case '2':
							col_back = BG_BLUE;
							break;
						case '3':
							col_back = BG_GREEN;
							break;
						case '4':
							col_back = BG_BLACK;
							break;
						case '5':
							col_back = BG_WHITE;
							break;
						case '6':
							col_back = BG_MAGENTA;
							break;
						case '7':
							col_back = BG_YELLOW;
							break;
						case '8':
							col_back = BG_CYAN;
							break;
						case '+':
						case '=':
							col_attr = CL_RANDOM;
							col_back = BG_RANDOM;
							break;
						}
						strcpy(temp, make_colour());
						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, CL_DEFAULT);
				col_attr = CL_CLEAR;
				col_fore = FG_NONE;
				col_back = BG_NONE;
			}
			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)
		{
			char buf[MSL];
			const char *ptr;
			int shown_lines = 0;
			int total_lines = 0;

			for (ptr = d->showstr_head; ptr != d->showstr_point; ptr++)
				if (*ptr == '\n')
					shown_lines++;

			total_lines = shown_lines;
			for (ptr = d->showstr_point; *ptr != '\0'; ptr++)
				if (*ptr == '\n')
					total_lines++;

			sprintf(buf,
					"\n\r(%d%%) Please type (H)elp, (R)efresh, (B)ack, or (C)ontinue or hit ENTER.\n\r",
					100 * shown_lines / total_lines);
			write_to_buffer(d, buf, 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)
				&& (d->fcommand || ch->fighting || d->editor > 0))
				write_to_buffer(d, "\n\r", 2);

			if (IS_SET(ch->comm, COMM_PROMPT))
			{
				if ((d->fcommand && !d->run_buf) || ch->fighting)
					bust_a_prompt(d->character);
				else if (d->editor > 0)
				{
					chprintlnf(ch, "{cOLC %s : {W%s{x",
							   olc_ed_name(d->character),
							   olc_ed_vnum(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 (gquest_info.running != GQUEST_OFF)
				sprintf(buf2, "%d", gquest_info.timer);
			else
				sprintf(buf2, "%dn", gquest_info.timer);
			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)
{
	CLAN_DATA *clan;

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

	/* check clans */
	for (clan = clan_first; clan; clan = clan->next)
	{
		if (LOWER(name[0]) == LOWER(clan->name[0]) &&
			!str_cmp(name, 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_first; 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_first; 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)
{
	DESCRIPTOR_DATA *d;

	if (txt == NULL || (d = ch->desc) == NULL)
		return;

	if (ch->lines == 0)
	{
		write_to_buffer(d, txt, strlen(txt));
		return;
	}

	/*
	 * If there is already some data being "paged" for this descriptor,
	 * append the new string.
	 */
	if (!IS_NULLSTR(d->showstr_head))
	{
		char *fub;
		int i;
		int size_new = strlen(txt) + strlen(d->showstr_head) + 2;

		alloc_mem(fub, char, size_new);
		fub[0] = '\0';
		strncat(fub, d->showstr_head, size_new);
		i = strlen(fub) - strlen(d->showstr_point);
		strncat(fub, txt, size_new);

		replace_string(d->showstr_head, fub);
		d->showstr_point = d->showstr_head + i;
		free_mem(fub);
		return;
	}

	replace_string(d->showstr_head, txt);
	d->showstr_point = d->showstr_head;
	show_string(d, "");
	return;
}

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

	one_argument(input, buf);

	switch (UPPER(buf[0]))
	{
	case '\0':
	case 'C':
		lines = 0;
		break;

	case 'R':					/* refresh current page of text */
		lines = -1 - (d->character->lines);
		break;

	case 'B':					/* scroll back a page of text */
		lines = -(2 * (int) d->character->lines);
		break;

	case '?':
	case 'H':					/* Show some help */
		write_to_buffer(d, "Pager help:\n\r" "C or Enter     next page\n\r"
						"R              refresh this page\n\r", 0);
		write_to_buffer(d,
						"B              previous page\n\r"
						"H or ?         help\n\r" "Any other keys exit.\n\r",
						0);
		return;

	default:					/*otherwise, stop the text viewing */
		if (d->showstr_head)
		{
			replace_string(d->showstr_head, "");
		}
		d->showstr_point = NULL;
		return;

	}

	/* do any backing up necessary */
	if (lines < 0)
	{
		for (scan = d->showstr_point; scan > d->showstr_head; scan--)
			if ((*scan == '\n') || (*scan == '\r'))
			{
				toggle = -toggle;
				if (toggle < 0)
					if (!(++lines))
						break;
			}
		d->showstr_point = scan;
	}

	/* show a chunk */
	lines = 0;
	toggle = 1;
	for (scan2 = buffer;; scan2++, d->showstr_point++)
		if (((*scan2 = *d->showstr_point) == '\n' || *scan2 == '\r')
			&& (toggle = -toggle) < 0)
			lines++;
		else if (!*scan2
				 || (d->character && !IS_NPC(d->character)
					 && lines >= (int) d->character->lines))
		{

			*scan2 = '\0';
			write_to_buffer(d, buffer, strlen(buffer));

			/* See if this is the end (or near the end) of the string */
			for (chk = d->showstr_point; isspace(*chk); chk++);
			if (!*chk)
			{
				if (d->showstr_head)
				{
					replace_string(d->showstr_head, "");
				}
				d->showstr_point = 0;
			}
			return;
		}

	return;
}

char *fname(const char *namelist)
{
	static char holder[256];
	char *point;

	for (point = holder; isalpha(*namelist); namelist++, point++)
		*point = *namelist;

	*point = '\0';

	return (holder);
}

void perform_act(const char *orig, CHAR_DATA * ch, const void *arg1,
				 const void *arg2, flag_t type, CHAR_DATA * to)
{
	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_DATA *vch = (CHAR_DATA *) arg2;
	OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
	OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
	const char *str, *i = NULL;
	char *point;
	char buf[MSL];

	point = buf;
	str = orig;

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

		++str;
		i = "<@@@>";
		if (!arg2 && *str >= 'A' && *str <= 'Z')
		{
			logf("perform_act:missing arg2 for code %d.", *str);
			i = " <@@@> ";
		}
		else
		{
			switch (*str)
			{
			default:
				logf("perform_act:bad code %c.", *str);
				i = " <@@@> ";
				break;

			case '$':
				i = "$";
				break;
			case 't':
				if (arg1)
				{
					if (IS_SET(type, TO_DAMAGE)
						&& (IS_NPC(to) || !IS_SET(to->act, PLR_AUTODAMAGE)))
						i = &str_empty[0];
					else
						i = (const char *) arg1;
				}
				else
					log_string("perform_act:bad code $t for 'arg1'");
				break;
			case 'T':
				if (arg2)
					i = (const char *) arg2;
				else
					log_string("perform_act:bad code $T for 'arg2'");
				break;
			case 'n':
				if (ch && to)
					i = PERS(ch, to);
				else
					log_string("perform_act:bad code $n for 'ch' or 'to'");
				break;
			case 'N':
				if (vch && to)
					i = PERS(vch, to);
				else
					log_string("perform_act:bad code $N for 'ch' or 'to'");
				break;
			case 'e':
				if (ch)
					i = he_she[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $e for 'ch'");
				break;
			case 'E':
				if (vch)
					i = he_she[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $E for 'vch'");
				break;
			case 'm':
				if (ch)
					i = him_her[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $m for 'ch'");
				break;
			case 'M':
				if (vch)
					i = him_her[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $M for 'vch'");
				break;
			case 's':
				if (ch)
					i = his_her[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $s for 'ch'");
				break;
			case 'S':
				if (vch)
					i = his_her[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $S for 'vch'");
				break;
			case 'g':
				if (ch && ch->deity != NULL)
					i = ch->deity->name;
				else
					log_string("perform_act:bad code $g for 'ch'");
				break;
			case 'G':
				if (vch && vch->deity != NULL)
					i = vch->deity->name;
				else
					log_string("perform_act:bad code $G for 'vch'");
				break;
			case 'c':
				if (ch && ch->clan != NULL)
					i = ch->clan->name;
				else
					log_string("perform_act:bad code $c for 'ch'");
				break;
			case 'C':
				if (vch && vch->clan != NULL)
					i = vch->clan->name;
				else
					log_string("perform_act:bad code $C for 'vch'");
				break;
			case 'o':
				if (to && obj1)
					i = can_see_obj(to, obj1) ? fname(obj1->name) : "something";
				else
					log_string("perform_act:bad code $o for 'to' and 'obj1'");
				break;

			case 'O':
				if (to && obj2)
					i = can_see_obj(to, obj2) ? fname(obj2->name) : "something";
				else
					log_string("perform_act:bad code $O for 'to' and 'obj2'");
				break;

			case 'p':
				if (to && obj1)
					i = can_see_obj(to, obj1) ? obj1->short_descr : "something";
				else
					log_string("perform_act:bad code $p for 'to' and 'obj1'");
				break;

			case 'P':
				if (to && obj2)
					i = can_see_obj(to, obj2) ? obj2->short_descr : "something";
				else
					log_string("perform_act:bad code $P for 'to' and 'obj2'");
				break;

			case 'd':
				if (arg2 == NULL || ((const char *) arg2)[0] == '\0')
				{
					i = "door";
				}
				else
				{
					char name[MIL];

					one_argument((const char *) arg2, name);
					i = name;
				}
				break;
			}
		}

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

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

	if (ch && ch->in_room && IS_SET(type, TO_ROOM | TO_NOTVICT))
	{
		OBJ_DATA *obj, *obj_next;
		CHAR_DATA *tch, *tch_next;

		point = buf;
		str = orig;
		while (*str != '\0')
		{
			*point++ = *str++;
		}
		*point = '\0';

		for (obj = ch->in_room->first_content; obj; obj = obj_next)
		{
			obj_next = obj->next_content;
			if (HAS_TRIGGER_OBJ(obj, TRIG_ACT))
				p_act_trigger(buf, NULL, obj, NULL, ch, NULL, NULL, TRIG_ACT);
		}

		for (tch = ch; tch; tch = tch_next)
		{
			tch_next = tch->next_in_room;

			for (obj = tch->first_carrying; obj; obj = obj_next)
			{
				obj_next = obj->next_content;
				if (HAS_TRIGGER_OBJ(obj, TRIG_ACT))
					p_act_trigger(buf, NULL, obj, NULL, ch, NULL, NULL,
								  TRIG_ACT);
			}
		}

		if (HAS_TRIGGER_ROOM(ch->in_room, TRIG_ACT))
			p_act_trigger(buf, NULL, NULL, ch->in_room, ch, NULL, NULL,
						  TRIG_ACT);
	}

	return;
}

const char *perform_act_string(const char *orig, CHAR_DATA * ch,
							   const void *arg1, const void *arg2, bool cReturn)
{
	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_DATA *vch = (CHAR_DATA *) arg2;
	OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
	OBJ_DATA *obj2 = (OBJ_DATA *) arg2;
	const char *str, *i = NULL;
	char *point;
	static char buf[MSL];

	point = buf;
	str = orig;

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

		++str;
		i = "<@@@>";
		if (!arg2 && *str >= 'A' && *str <= 'Z')
		{
			logf("perform_act:missing arg2 for code %d.", *str);
			i = " <@@@> ";
		}
		else
		{
			switch (*str)
			{
			default:
				logf("perform_act:bad code %c.", *str);
				i = " <@@@> ";
				break;

			case '$':
				i = "$";
				break;
			case 't':
				if (arg1)
				{
					i = (const char *) arg1;
				}
				else
					log_string("perform_act:bad code $t for 'arg1'");
				break;
			case 'T':
				if (arg2)
					i = (const char *) arg2;
				else
					log_string("perform_act:bad code $T for 'arg2'");
				break;
			case 'n':
				if (ch)
					i = IS_NPC(ch) ? ch->short_descr : ch->name;
				else
					log_string("perform_act:bad code $n for 'ch'");
				break;
			case 'N':
				if (vch)
					i = IS_NPC(vch) ? vch->short_descr : vch->name;
				else
					log_string("perform_act:bad code $N for 'vch'");
				break;
			case 'e':
				if (ch)
					i = he_she[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $e for 'ch'");
				break;
			case 'E':
				if (vch)
					i = he_she[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $E for 'vch'");
				break;
			case 'm':
				if (ch)
					i = him_her[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $m for 'ch'");
				break;
			case 'M':
				if (vch)
					i = him_her[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $M for 'vch'");
				break;
			case 's':
				if (ch)
					i = his_her[URANGE(0, ch->sex, 2)];
				else
					log_string("perform_act:bad code $s for 'ch'");
				break;
			case 'S':
				if (vch)
					i = his_her[URANGE(0, vch->sex, 2)];
				else
					log_string("perform_act:bad code $S for 'vch'");
				break;
			case 'g':
				if (ch && ch->deity != NULL)
					i = ch->deity->name;
				else
					log_string("perform_act:bad code $g for 'ch'");
				break;
			case 'G':
				if (vch && vch->deity != NULL)
					i = vch->deity->name;
				else
					log_string("perform_act:bad code $G for 'vch'");
				break;
			case 'c':
				if (ch && ch->clan != NULL)
					i = ch->clan->name;
				else
					log_string("perform_act:bad code $c for 'ch'");
				break;
			case 'C':
				if (vch && vch->clan != NULL)
					i = vch->clan->name;
				else
					log_string("perform_act:bad code $C for 'vch'");
				break;
			case 'o':
				if (obj1)
					i = fname(obj1->name);
				else
					log_string("perform_act:bad code $o for 'to' and 'obj1'");
				break;

			case 'O':
				if (obj2)
					i = fname(obj2->name);
				else
					log_string("perform_act:bad code $O for 'obj2'");
				break;

			case 'p':
				if (obj1)
					i = obj1->short_descr;
				else
					log_string("perform_act:bad code $p for 'obj1'");
				break;

			case 'P':
				if (obj2)
					i = obj2->short_descr;
				else
					log_string("perform_act:bad code $P for 'obj2'");
				break;

			case 'd':
				if (arg2 == NULL || ((const char *) arg2)[0] == '\0')
				{
					i = "door";
				}
				else
				{
					char name[MIL];

					one_argument((const char *) arg2, name);
					i = name;
				}
				break;
			}
		}

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

	*point++ = '{';
	*point++ = 'x';
	if (cReturn)
	{
		*point++ = '\n';
		*point++ = '\r';
	}
	*point = '\0';
	buf[0] = UPPER(buf[0]);

	return buf;
}

#define SENDOK(ch, type)    ((IS_NPC(ch) || ((ch)->desc && (ch->desc->connected == CON_PLAYING))) \
            && ((ch)->position >= min_pos))

void act_new(const char *format, CHAR_DATA * ch, const void *arg1,
			 const void *arg2, flag_t type, int min_pos)
{
	DESCRIPTOR_DATA *d;
	ROOM_INDEX_DATA *room;
	CHAR_DATA *to = (CHAR_DATA *) arg2;

	if (!format || !*format)
		return;

	if (IS_SET(type, TO_CHAR))
	{
		if (ch && SENDOK(ch, type))
			perform_act(format, ch, arg1, arg2, type, ch);
	}

	if (IS_SET(type, TO_VICT))
	{
		if (to && SENDOK(to, type) && to != ch)
			perform_act(format, ch, arg1, arg2, type, to);
	}

	if (IS_SET(type, TO_ZONE | TO_ALL))
	{
		for (d = descriptor_first; d; d = d->next)
		{
			CHAR_DATA *vch = CH(d);

			if (vch && SENDOK(vch, type) && (vch != ch)
				&&
				((IS_SET
				  (type, TO_ALL) || (vch->in_room
									 && vch->in_room->area ==
									 ch->in_room->area))))
				perform_act(format, ch, arg1, arg2, type, vch);
		}
	}

	if (IS_SET(type, TO_ROOM | TO_NOTVICT))
	{
		OBJ_DATA *obj1 = (OBJ_DATA *) arg1;
		OBJ_DATA *obj2 = (OBJ_DATA *) arg2;

		if (ch && ch->in_room != NULL)
			room = ch->in_room;
		else if (obj1 && obj1->in_room != NULL)
			room = obj1->in_room;
		else if (obj2 && obj2->in_room != NULL)
			room = obj2->in_room;
		else
		{
			bugf("no valid target '%s'", format);
			return;
		}
		for (to = room->first_person; to; to = to->next_in_room)
		{
			if (SENDOK(to, type) && (to != ch)
				&& (IS_SET(type, TO_ROOM) || (to != (CHAR_DATA *) arg2)))
				perform_act(format, ch, arg1, arg2, type, to);
		}
	}
	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);
}

void update_last_func(DESCRIPTOR_DATA * d, const char *lstr, const char *sstr)
{
#if !defined(WIN32) && !defined(__CYGWIN__)
	last_descriptor = d;
	if (lstr != NULL)
		strcpy(last_func_long, lstr);
	if (sstr != NULL)
		strcpy(last_func_short, sstr);
#endif
}

#if !defined(WIN32) && !defined(__CYGWIN__)

#define	CORE_EXAMINE_SCRIPT	"../corefiles/gdbscript"

void halt_mud(int sig)
{
	DESCRIPTOR_DATA *d;
	CHAR_DATA *ch;
	struct sigaction default_action;
	int i;
	pid_t forkpid;
	int status;
	char message[MSL];

	waitpid(-1, &status, WNOHANG);
	if (!crashed)
	{
		crashed++;
		logf("GAME CRASHED (SIGNAL %d, %s).", sig, strsignal(sig));
		logf("Last recored function: %s", last_func_long);
		// Inform last command typer that he caused the crash
		if (last_descriptor && !IS_NULLSTR(last_func_short))
		{
			write_to_descriptor(last_descriptor,
								"\n\rThe last command you typed, '", 0);
			write_to_descriptor(last_descriptor, last_func_short, 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);
		}
		sprintf(message,
				"\n\r---CRASH INFORMATION---\n\rSignal %d (%s)\n\rLast recorded function: %s\n\rDetails: %s\n\n",
				sig, strsignal(sig),
				!IS_NULLSTR(last_func_short) ? last_func_short : "<unknown>",
				last_func_long);

		for (d = descriptor_first; d != NULL; d = d_next)
		{
			d_next = d->next;
			ch = CH(d);
			if (!ch)
			{
				close_socket(d);
				continue;
			}
			if (IS_NPC(ch))
				continue;

			if ((sig == SIGVTALRM || sig == SIGALRM) && IS_IMMORTAL(ch))
				write_to_descriptor(d,
									"\n\rThe mud has been unresponsive for 60 seconds.  Rebooting.\n\r",
									0);

			write_to_descriptor(d, "\n\rThe mud has CRASHED.\007\n\r", 0);

			if (IS_IMMORTAL(ch))
				write_to_descriptor(d, message, 0);
		}

		// try to save all characters - save_char_obj has sanity checking
		for (d = descriptor_first; 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, &status, WNOHANG);
			// 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);

		// Run gdb script.
		if (!fork())
		{
			execl(CORE_EXAMINE_SCRIPT, CORE_EXAMINE_SCRIPT, (char *) NULL);
			exit(0);
		}
		else
			return;
		raise(sig);
	}

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

		for (d = descriptor_first; 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;
		}
		log_string("CHARACTERS NOT SAVED.");
		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++;
		log_string("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

void set_game_levels(int Old, int New)
{
	CMD_DATA *cmd;
	HELP_DATA *pHelp;
	OBJ_INDEX_DATA *pObj;
	MOB_INDEX_DATA *pMob;
	BAN_DATA *ban;
	DISABLED_DATA *d;
	MBR_DATA *mbr;
	int hash, sn, x;
	int diff = MAX_LEVEL - LEVEL_IMMORTAL;
	int imm_level = Old - diff;
	int mod = New - Old;

	logf("Old Imm Level = %d, Old Max Level = %d.", imm_level, Old);
	logf("New Imm Level = %d, New Max Level = %d.", New - diff, New);

	for (cmd = cmd_first; cmd; cmd = cmd->next)
	{
		if (cmd->level >= imm_level)
			cmd->level += mod;
	}
	save_commands();
	for (pHelp = help_first; pHelp; pHelp = pHelp->next)
	{
		if (pHelp->level >= imm_level)
			pHelp->level += mod;
	}
	save_helps();
	for (hash = 0; hash < MAX_KEY_HASH; hash++)
	{
		for (pMob = mob_index_hash[hash]; pMob; pMob = pMob->next)
		{
			if (pMob->level >= imm_level)
			{
				pMob->level += mod;
				SET_BIT(pMob->area->area_flags, AREA_CHANGED);
			}
		}
		for (pObj = obj_index_hash[hash]; pObj; pObj = pObj->next)
		{
			if (pObj->level >= imm_level)
			{
				pObj->level += mod;
				SET_BIT(pObj->area->area_flags, AREA_CHANGED);
			}
		}
	}
	do_asave(NULL, "changed");
	for (ban = ban_first; ban; ban = ban->next)
	{
		if (ban->level >= imm_level)
			ban->level += mod;
	}
	save_bans();
	for (d = disabled_first; d; d = d->next)
	{
		if (d->level >= imm_level)
			d->level += mod;
	}
	save_disabled();
	for (sn = 0; sn < maxSkill; sn++)
	{
		for (x = 0; x < maxClass; x++)
		{
			if (skill_table[sn].skill_level[x] >= imm_level)
				skill_table[sn].skill_level[x] += mod;
		}
	}
	save_skills();
	for (mbr = mbr_first; mbr; mbr = mbr->next)
	{
		if (mbr->level >= imm_level)
			mbr->level += mod;
	}
	save_members();
}

CH_CMD(do_crash)
{
	if (IS_NULLSTR(argument))
	{
		chprintln(ch, "Syntax: crash confirm - send a SIGSEGV to the mud.");
		chprintln(ch, "      : crash loop    - start an infinite loop.");
		return;
	}

	if (!str_cmp(argument, "loop"))
	{
		for (;;);
		return;
	}
	else if (!str_cmp(argument, "confirm"))
	{
		raise(SIGSEGV);
		return;
	}
	else
	{
		do_crash(ch, "");
		return;
	}
}