wileymud-1.187b/
wileymud-1.187b/attic/
wileymud-1.187b/attic/bin/
wileymud-1.187b/attic/lib/
wileymud-1.187b/attic/lib/adm/
wileymud-1.187b/attic/lib/man/
wileymud-1.187b/attic/lib/new-wld/
wileymud-1.187b/attic/lib/new-wld/default/
wileymud-1.187b/attic/lib/old/
wileymud-1.187b/attic/lib/wld/
wileymud-1.187b/attic/public_html/
wileymud-1.187b/attic/public_html/gfx/
wileymud-1.187b/attic/src/bin/
wileymud-1.187b/attic/src/etc/
wileymud-1.187b/attic/src/libauth-4.0-p5/
wileymud-1.187b/attic/src/sedna/
wileymud-1.187b/backups/
wileymud-1.187b/bin/
wileymud-1.187b/docs/
wileymud-1.187b/etc/
wileymud-1.187b/lib/
wileymud-1.187b/lib/adm/
wileymud-1.187b/lib/boards/
wileymud-1.187b/lib/log/
wileymud-1.187b/lib/man/
wileymud-1.187b/lib/ply/
wileymud-1.187b/lib/ply/a/
wileymud-1.187b/lib/ply/b/
wileymud-1.187b/lib/ply/c/
wileymud-1.187b/lib/ply/d/
wileymud-1.187b/lib/ply/g/
wileymud-1.187b/lib/ply/k/
wileymud-1.187b/lib/ply/m/
wileymud-1.187b/lib/ply/s/
wileymud-1.187b/lib/ply/t/
wileymud-1.187b/public_html/gfx/
wileymud-1.187b/src/bin/
wileymud-1.187b/src/convert/attic/
wileymud-1.187b/src/convert/obj/
wileymud-1.187b/src/convert/perl/
wileymud-1.187b/src/convert/perl/MudConvert/
wileymud-1.187b/src/convert/perl/MudConvert/DUMP/
wileymud-1.187b/src/convert/perl/MudConvert/Report/
wileymud-1.187b/src/convert/perl/MudConvert/WileyMUD/
wileymud-1.187b/src/convert/perl/output/
wileymud-1.187b/src/convert/perl/output/DUMP/
wileymud-1.187b/src/convert/perl/output/Report/
wileymud-1.187b/src/convert/perl/output/WileyMUD/
wileymud-1.187b/src/etc/
wileymud-1.187b/src/etc/init.d/
wileymud-1.187b/src/etc/rc.d/
wileymud-1.187b/src/etc/rc.d/init.d/
wileymud-1.187b/src/lib/
wileymud-1.187b/src/lib/adm/
wileymud-1.187b/src/lib/boards/
wileymud-1.187b/src/lib/log/
wileymud-1.187b/src/lib/man/
wileymud-1.187b/src/lib/ply/
wileymud-1.187b/src/lib/ply/a/
wileymud-1.187b/src/lib/ply/b/
wileymud-1.187b/src/lib/ply/c/
wileymud-1.187b/src/lib/ply/d/
wileymud-1.187b/src/lib/ply/e/
wileymud-1.187b/src/lib/ply/f/
wileymud-1.187b/src/lib/ply/g/
wileymud-1.187b/src/lib/ply/h/
wileymud-1.187b/src/lib/ply/i/
wileymud-1.187b/src/lib/ply/j/
wileymud-1.187b/src/lib/ply/k/
wileymud-1.187b/src/lib/ply/l/
wileymud-1.187b/src/lib/ply/m/
wileymud-1.187b/src/lib/ply/n/
wileymud-1.187b/src/lib/ply/o/
wileymud-1.187b/src/lib/ply/p/
wileymud-1.187b/src/lib/ply/q/
wileymud-1.187b/src/lib/ply/r/
wileymud-1.187b/src/lib/ply/s/
wileymud-1.187b/src/lib/ply/t/
wileymud-1.187b/src/lib/ply/u/
wileymud-1.187b/src/lib/ply/v/
wileymud-1.187b/src/lib/ply/w/
wileymud-1.187b/src/lib/ply/x/
wileymud-1.187b/src/lib/ply/y/
wileymud-1.187b/src/lib/ply/z/
wileymud-1.187b/src/obj/
wileymud-1.187b/src/utils/
wileymud-1.187b/src/utils/mobmaker/
/*
 *  file: comm.c , Communication module.                   Part of DIKUMUD
 *  Usage: Communication, central game loop.
 *  Copyright (C) 1990, 1991 - see 'license.doc' for complete information.
 *  All Rights Reserved
 *  Using *any* part of DikuMud without having read license.doc is
 *  violating our copyright.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/resource.h>

#include "global.h"
#ifdef I3
#include "i3.h"
#endif
#ifdef IMC
#include "imc.h"
#endif
#include "bug.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "modify.h"
#include "whod.h"
#include "multiclass.h"
#include "weather.h"
#include "mudlimits.h"
#include "spells.h"
#include "spell_parser.h"
#include "sound.h"
#include "fight.h"
#include "mob_actions.h"
#include "act_other.h"
#include "signals.h"
#include "ban.h"
#include "board.h"
#define _COMM_C
#include "comm.h"

#ifdef RFC1413
#include "libident-0.19/ident.h"
#endif

struct descriptor_data                 *descriptor_list = NULL;
struct descriptor_data                 *next_to_process = NULL;

int                                     mud_port = 0;
int                                     slow_death = 0;	       /* Shut her down, Martha, she's sucking mud */
int                                     diku_shutdown = 0;     /* clean shutdown */
int                                     diku_reboot = 0;       /* reboot the game after a shutdown */
int                                     DEBUG = FALSE;
int                                     no_specials = 0;       /* Suppress ass. of special routines */
long                                    Uptime = 0L;	       /* time that the game has been up */

int                                     maxdesc = 0;
int                                     avail_descs = 0;
int                                     tics = 0;	       /* for extern checkpointing */
int                                     pulse = 0;
int                                     pulse_update = PULSE_UPDATE + PULSE_VARIABLE;
int                                     pulse_river = PULSE_RIVER;
int                                     pulse_teleport = PULSE_TELEPORT;
int                                     pulse_nature = PULSE_NATURE;
int                                     pulse_sound = PULSE_SOUND;
int                                     pulse_zone = PULSE_ZONE;
int                                     pulse_mobile = PULSE_MOBILE;
int                                     pulse_violence = PULSE_VIOLENCE;
int                                     pulse_reboot = PULSE_REBOOT;
int                                     pulse_dump = PULSE_DUMP;

int main(int argc, const char **argv)
{
    int                                     port = 0;
    int                                     pos = 1;
    int                                     exit_code = 0;
    const char                             *dir = NULL;
    const char                             *logfile = NULL;
    const char                             *pidfile = NULL;

    if (DEBUG > 1)
	log_info("called %s with %d, %08zx", __PRETTY_FUNCTION__, argc, (size_t) argv);

    port = DFLT_PORT;
    dir = DFLT_DIR;
    WizLock = FALSE;

    while ((pos < argc) && (*(argv[pos]) == '-')) {
	switch (*(argv[pos] + 1)) {
	    case 'w':
		WizLock = TRUE;
		log_info("WizLock is SET.");
		break;
	    case 'D':
		DEBUG = TRUE;
		log_info("Debugging is on.");
		break;
	    case 'l':
		log_info("Lawful mode no longer available.");
		break;
	    case 'L':
		if (*(argv[pos] + 2))
		    logfile = argv[pos] + 2;
		else if (++pos < argc)
		    logfile = argv[pos];
		else {
		    log_fatal("LOG filename expected after option -L.");
		    proper_exit(MUD_HALT);
		}
		break;
	    case 'P':
		if (*(argv[pos] + 2))
		    pidfile = argv[pos] + 2;
		else if (++pos < argc)
		    pidfile = argv[pos];
		else {
		    log_fatal("PID filename expected after option -P.");
		    proper_exit(MUD_HALT);
		}
		break;
	    case 'd':
		if (*(argv[pos] + 2))
		    dir = argv[pos] + 2;
		else if (++pos < argc)
		    dir = argv[pos];
		else {
		    log_fatal("Directory arg expected after option -d.");
		    proper_exit(MUD_HALT);
		}
		break;
	    case 's':
		no_specials = 1;
		log_info("Suppressing assignment of special routines.");
		break;
	    default:
		log_info("Unknown option -% in argument string.", *(argv[pos] + 1));
		break;
	}
	pos++;
    }

    if (pos < argc) {
	if (!isdigit(*argv[pos])) {
	    log_fatal("Usage: %s [-l] [-s] [-d pathname] [ port # ]\n", argv[0]);
	    proper_exit(MUD_HALT);
	} else if ((port = atoi(argv[pos])) <= 1024) {
	    log_fatal("Illegal port #\n");
	    proper_exit(MUD_HALT);
	}
    }

    Uptime = time(0);
    mud_port = port;
    log_boot("Running game on port %d.", port);

    if (chdir(dir) < 0) {
	log_fatal("Cannot change directory to %s", dir);
	proper_exit(MUD_HALT);
    }
    log_boot("Using %s as data directory.", dir);

    if (pidfile) {
	FILE                                   *pidfp = NULL;

	if (!(pidfp = fopen(pidfile, "w"))) {
	    log_fatal("Cannot open PID file %s", pidfile);
	    proper_exit(MUD_HALT);
	}
	fprintf(pidfp, "%d\n", getpid());
	fclose(pidfp);
	log_boot("PID written to %s", pidfile);
    }

    if (logfile) {
	log_boot("Switching to %s as stderr.", logfile);
	stderr = freopen(logfile, "a", stderr);
	if (!stderr) {
	    log_fatal("Cannot reopen stderr!");
	    proper_exit(MUD_HALT);
	}
	close(fileno(stdout));
	dup2(fileno(stderr), fileno(stdout));
	log_boot("Switch to %s completed.", logfile);
    }

    srandom(time(0));

#if 0
    if (init_sql()) {
	log_boot("Connected to database!");
	log_boot("%s\n", version_sql());
	log_boot("Disconnecting from database!");
	close_sql();
    } else {
	log_fatal("%s\n", "Couldn't open Database Connection!  Aborting!");
	return MUD_HALT;
    }
#endif

    exit_code = run_the_game(port);
    return exit_code;
}

/* Init sockets, run game, and cleanup sockets */
int run_the_game(int port)
{
    int                                     s = 0;

    if (DEBUG > 1)
	log_info("called %s with %d", __PRETTY_FUNCTION__, port);

    descriptor_list = NULL;

    log_boot("Signal trapping.");
    signal_setup();

    log_boot("Opening mother connection.");
    s = init_socket(port);
    load_db();

    log_boot("Opening WHO port.");
    init_whod(port);

#ifdef IMC
    log_boot("Opening I3 connection.");
    i3_startup(FALSE, 3000, FALSE);
#endif
#ifdef IMC
    log_boot("Opening IMC2 connection.");
    imc_startup(FALSE, -1, FALSE);
#endif

    log_boot("Entering game loop.");
    game_loop(s);

#ifdef IMC
    imc_shutdown(FALSE);
#endif
#ifdef I3
    i3_shutdown(0);
#endif
    close_sockets(s);
    close_whod();

    unload_db();
    if (diku_reboot) {
	log_boot("Rebooting.");
	return MUD_REBOOT;
    }
    log_boot("Normal termination of game.");
    return MUD_HALT;					       /* what's so great about HHGTTG, anyhow? */
}

/* Accept new connects, relay commands, and call 'heartbeat-functs' */
void game_loop(int s)
{
    fd_set                                  input_set;
    fd_set                                  output_set;
    fd_set                                  exc_set;
    struct timeval                          last_time;
    struct timeval                          now;
    struct timeval                          timespent;
    struct timeval                          timeout;
    struct timeval                          null_time;
    static struct timeval                   opt_time;
    char                                    comm[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    char                                    promptbuf[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    struct descriptor_data                 *point = NULL;
    struct descriptor_data                 *next_point = NULL;
    struct room_data                       *rm = NULL;
    struct char_data                       *mount = NULL;

    /*
     * int mask = 0; 
     */
    sigset_t                                mask;

    // int sql_reconnect;

    if (DEBUG > 1)
	log_info("called %s with %d", __PRETTY_FUNCTION__, s);

    pulse = 0;
    null_time.tv_sec = 0;
    null_time.tv_usec = 0;

    opt_time.tv_usec = OPT_USEC;			       /* Init time values */
    opt_time.tv_sec = 0;

    gettimeofday(&last_time, (struct timezone *)0);
    maxdesc = s;
    avail_descs = getdtablesize() - 2;			       /* !! Change if more needed !! */

/*
  mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
    sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) |
    sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP);
*/
    sigemptyset(&mask);
    sigaddset(&mask, SIGUSR1);
    sigaddset(&mask, SIGUSR2);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGPIPE);
    sigaddset(&mask, SIGALRM);
    sigaddset(&mask, SIGTERM);
    sigaddset(&mask, SIGHUP);
    sigaddset(&mask, SIGURG);
    sigaddset(&mask, SIGXCPU);

    /*
     * Main loop 
     */
    while (!diku_shutdown) {
	FD_ZERO(&input_set);
	FD_ZERO(&output_set);
	FD_ZERO(&exc_set);
	FD_SET(s, &input_set);
	for (point = descriptor_list; point; point = point->next) {
	    if (point->descriptor != 0) {
		FD_SET(point->descriptor, &input_set);
		FD_SET(point->descriptor, &exc_set);
		FD_SET(point->descriptor, &output_set);
	    }
	}

	gettimeofday(&now, (struct timezone *)0);
	timespent = timediff(&now, &last_time);
	timeout = timediff(&opt_time, &timespent);
	last_time.tv_sec = now.tv_sec + timeout.tv_sec;
	last_time.tv_usec = now.tv_usec + timeout.tv_usec;
	if (last_time.tv_usec >= 1000000) {
	    last_time.tv_usec -= 1000000;
	    last_time.tv_sec++;
	}

	/*
	 * sigsetmask(mask); 
	 */
	sigprocmask(SIG_BLOCK, &mask, NULL);
	whod_loop();
	if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) {
	    log_error("Select poll");
	    return;
	}
	if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
	    log_error("Select sleep");
	    /*
	     * proper_exit(MUD_HALT); 
	     */
	}
	/*
	 * sigsetmask(0); 
	 */
	sigprocmask(SIG_UNBLOCK, &mask, NULL);

	/*
	 * Respond to whatever might be happening 
	 */

	/*
	 * New connection? 
	 */
	if (FD_ISSET(s, &input_set))
	    if (new_descriptor(s) < 0)
		log_info("New connection");

	/*
	 * kick out the freaky folks 
	 */
	for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    if (FD_ISSET(point->descriptor, &exc_set)) {
		FD_CLR(point->descriptor, &input_set);
		FD_CLR(point->descriptor, &output_set);
		close_socket(point);
	    }
	}

	for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    if (FD_ISSET(point->descriptor, &input_set))
		if (process_input(point) < 0)
		    close_socket(point);
	}

	/*
	 * process_commands; 
	 */
	for (point = descriptor_list; point; point = next_to_process) {
	    next_to_process = point->next;
	    if ((--(point->wait) <= 0) && get_from_q(&point->input, comm)) {
		if (point->character && point->connected == CON_PLAYING &&
		    point->character->specials.was_in_room != NOWHERE) {
		    if (point->character->in_room != NOWHERE)
			char_from_room(point->character);
		    char_to_room(point->character, point->character->specials.was_in_room);
		    point->character->specials.was_in_room = NOWHERE;
		    act("$n has returned.", TRUE, point->character, 0, 0, TO_ROOM);
		    log_auth(point->character, "RECONNECTED %s (%s@%s/%s)!",
			     GET_NAME(point->character), point->username, point->host,
			     point->ip);
		}
		point->wait = 1;
		if (point->character)			       /* This updates the idle ticker to say we're not idle */
		    point->character->specials.timer = 0;
		point->prompt_mode = 1;

		if (point->str)
		    string_add(point, comm);
		else if (!point->connected)
		    if (point->page_first)
			control_page(point, comm);
		    else
			command_interpreter(point->character, comm);
		else
		    nanny(point, comm);
	    }
#if 0
	    else {
		if (point->character &&
		    (GET_POS(point->character) != POSITION_FIGHTING) &&
		    (GET_HIT(point->character) >= GET_MAX_HIT(point->character)) &&
		    (GET_MANA(point->character) >= GET_MAX_MANA(point->character)) &&
		    (GET_MOVE(point->character) >= GET_MAX_MOVE(point->character))
		    )
		    point->prompt_mode = 0;		       /* This might only show prompts if a command was
							        * processed */
	    }
#endif
	}

	for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    if (point->page_first)
		show_page(point);
	}

	for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    if (FD_ISSET(point->descriptor, &output_set) && point->output.head) {
		if (process_output(point) < 0)
		    close_socket(point);
		else
		    point->prompt_mode = 1;
	    }
	}

	/*
	 * give the people some prompts 
	 */
	for (point = descriptor_list; point; point = point->next) {
	    if (point->prompt_mode) {
		/*
		 * Maybe we can somewhow not do prompts if nothing has happened (IE: no command entered 
		 */
		if (point->str)
		    write_to_descriptor(point->descriptor, "] ");
		else if (!point->connected) {
#if 0
		    if (point->showstr_point)
			write_to_descriptor(point->descriptor, "*** Press return or q ***");
#endif
		    if (point->page_first)
			write_to_descriptor(point->descriptor, "*** Press return or q ***");
		    else {
			bzero(promptbuf, MAX_INPUT_LENGTH);
			if (IS_IMMORTAL(point->character) && IS_PC(point->character)) {
			    if (MOUNTED(point->character)) {
				mount = MOUNTED(point->character);
				sprintf(promptbuf, "[%s has %d/%dh %d/%dv]\r\n",
					GET_SDESC(mount),
					GET_HIT(mount), GET_MAX_HIT(mount),
					GET_MOVE(mount), GET_MAX_MOVE(mount));
			    }
			    if (IS_SET(point->character->specials.act, PLR_STEALTH))
				sprintf(promptbuf + strlen(promptbuf), "S");
			    if (point->character->invis_level > 0)
				sprintf(promptbuf + strlen(promptbuf), "I=%d: ",
					point->character->invis_level);
			    rm = real_roomp(point->character->in_room);
			    sprintf(promptbuf + strlen(promptbuf),
				    "#%d - %s [#%d]> ", rm->zone, zone_table[rm->zone].name,
				    rm->number);
			    write_to_descriptor(point->descriptor, promptbuf);
/* OLD mobs didn't have classes.. this doesn't work anymore */
			} else if (IS_NPC(point->character) &&
				   (IS_SET(point->character->specials.act, ACT_POLYSELF) ||
				    IS_SET(point->character->specials.act, ACT_POLYOTHER))) {
			    sprintf(promptbuf, "P %d/%dh %d/%dv > ",
				    GET_HIT(point->character),
				    GET_MAX_HIT(point->character),
				    GET_MOVE(point->character), GET_MAX_MOVE(point->character));
			    write_to_descriptor(point->descriptor, promptbuf);
			} else if (IS_NPC(point->character) &&
				   IS_SET(point->character->specials.act, ACT_SWITCH)) {
			    sprintf(promptbuf, "*%s[#%d] in [#%d] %d/%dh %d/%dm %d/%dv > ",
				    NAME(point->character),
				    MobVnum(point->character),
				    point->character->in_room,
				    GET_HIT(point->character),
				    GET_MAX_HIT(point->character),
				    GET_MANA(point->character),
				    GET_MAX_MANA(point->character),
				    GET_MOVE(point->character), GET_MAX_MOVE(point->character));
			    write_to_descriptor(point->descriptor, promptbuf);
			} else {
			    if (MOUNTED(point->character)) {
				if (HasClass(point->character, CLASS_RANGER) ||
				    IS_AFFECTED(MOUNTED(point->character), AFF_CHARM)) {
				    mount = MOUNTED(point->character);
				    sprintf(promptbuf, "[%s has %d/%dh %d/%dv]\r\n",
					    GET_SDESC(mount),
					    GET_HIT(mount), GET_MAX_HIT(mount),
					    GET_MOVE(mount), GET_MAX_MOVE(mount));
				}
			    }
			    sprintf(promptbuf + strlen(promptbuf), "%d/%dh %d/%dm %d/%dv > ",
				    GET_HIT(point->character),
				    GET_MAX_HIT(point->character),
				    GET_MANA(point->character),
				    GET_MAX_MANA(point->character),
				    GET_MOVE(point->character), GET_MAX_MOVE(point->character));
			    write_to_descriptor(point->descriptor, promptbuf);
			}
		    }
		}
		point->prompt_mode = 0;
	    }
	}
/*
 * PULSE handling.... periodic events
 */

	if ((++pulse) > PULSE_MAX)
	    pulse = 0;

	if ((--pulse_zone) <= 0) {
	    pulse_zone = PULSE_ZONE;
	    zone_update();
	}
	if ((--pulse_teleport) <= 0) {
	    pulse_teleport = PULSE_TELEPORT;
	    Teleport(pulse);
	}
	if ((--pulse_nature) <= 0) {
	    pulse_nature = PULSE_NATURE;
	    check_all_nature(pulse);
	}
	if ((--pulse_violence) <= 0) {
	    pulse_violence = PULSE_VIOLENCE;
	    perform_violence(pulse);
	}
	if ((--pulse_mobile) <= 0) {
	    pulse_mobile = PULSE_MOBILE;
	    mobile_activity();
	}
	if ((--pulse_river) <= 0) {
	    pulse_river = PULSE_RIVER;
	    down_river(pulse);
	}
	if ((--pulse_sound) <= 0) {
	    pulse_sound = PULSE_SOUND;
	    MakeSound(pulse);
	}
	if ((--pulse_update) <= 0) {
	    pulse_update = PULSE_UPDATE + number(0, PULSE_VARIABLE);
	    weather_and_time(1);
	    affect_update();
	    point_update(pulse);
	}
	if ((--pulse_reboot) <= 0) {
	    pulse_reboot = PULSE_REBOOT;
	    check_reboot();
	}
	if ((--pulse_dump) <= 0) {
	    pulse_dump = PULSE_DUMP;
	    // dump_player_list();
	}
#if 0
	// This now verifies our connection is active, so we call it in
	// the loop to ensure we can reconnect if the db bounces.
	for (sql_reconnect = 0; 1; sql_reconnect++) {
	    if (verify_sql())
		break;
	    if (sql_reconnect > 10) {
		log_fatal("%s\n", "Database Connection LOST!  Aborting!");
		proper_exit(MUD_HALT);
	    }
	    sleep(1);
	}
#endif

#if 1
#ifdef I3
	i3_loop();
#endif
#ifdef IMC
	imc_loop();
#endif
#endif

	tics++;						       /* tics since last checkpoint signal */
    }
}

/*
 * general utility stuff (for local use)
 */
int get_from_q(struct txt_q *queue, char *dest)
{
    struct txt_block                       *tmp = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx, %s", __PRETTY_FUNCTION__, (size_t) queue, VNULL(dest));

    if (!queue) {
	log_info("Input from non-existant queue?");
	return 0;
    }
    if (!queue->head)
	return 0;
    tmp = queue->head;
    strcpy(dest, queue->head->text);
    queue->head = queue->head->next;
    DESTROY(tmp->text);
    DESTROY(tmp);
    return 1;
}

void write_to_q(const char *txt, struct txt_q *queue, int do_timestamp)
{
    struct txt_block                       *new = NULL;

#ifdef TIME_DEBUG
    struct timeval                          now;
    char                                    nowtime[26];
#endif

    /*
     * Cannot call things in bug.c from things bug.c calls!
     *
     * if (DEBUG > 2)
     *   log_info("called %s with %s, %08zx", __PRETTY_FUNCTION__, VNULL(txt), (size_t)queue);
     */

    if (!queue) {
	log_info("Output message to non-existant queue");
	return;
    }
#ifdef TIME_DEBUG
/* This is purely for debugging timing... don't leave this enabled! */
/* "Wed Jun 30 21:49:08 1993\n" */
/*             ^      ^         */
/*  0          11     18        */
    if (do_timestamp) {
	gettimeofday(&now, (struct timezone *)0);
	ctime_r((time_t *) & (now.tv_sec), nowtime);
    }
#endif

    CREATE(new, struct txt_block, 1);

#ifdef TIME_DEBUG
    if (do_timestamp) {
	CREATE(new->text, char, strlen          (txt) + 1 + 14);

	strncpy(new->text, nowtime + 11, 8);
	sprintf(new->text + 8, ".%03ld: ", now.tv_usec / 1000);
	strcat(new->text, txt);
    } else {
	CREATE(new->text, char, strlen          (txt) + 1);

	strcpy(new->text, txt);
    }
#else
    CREATE(new->text, char, strlen          (txt) + 1);

    strcpy(new->text, txt);
#endif

    if (!queue->head) {
	new->next = NULL;
	queue->head = queue->tail = new;
    } else {
	queue->tail->next = new;
	queue->tail = new;
	new->next = NULL;
    }
}

struct timeval timediff(struct timeval *a, struct timeval *b)
{
    struct timeval                          result;
    struct timeval                          tmp;

    if (DEBUG > 3)
	log_info("called %s with %08zx, %08zx", __PRETTY_FUNCTION__, (size_t) a, (size_t) b);

    tmp = *a;

    if ((result.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
	result.tv_usec += 1000000;
	--(tmp.tv_sec);
    }
    if ((result.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
	result.tv_usec = 0;
	result.tv_sec = 0;
    }
    return result;
}

/* Empty the queues before closing connection */
void flush_queues(struct descriptor_data *d)
{
    char                                    dummy[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) d);

    while (get_from_q(&d->output, dummy));
    while (get_from_q(&d->input, dummy));
}

/*
 * socket handling
 */

int init_socket(int port)
{
    int                                     opt = 1;
    char                                    hostname[MAX_HOSTNAME + 1] = "\0\0\0\0\0\0\0";
    struct sockaddr_in                      sa;
    struct hostent                         *hp = NULL;
    int                                     s = 0;
    int                                     i = 0;
    int                                     gotsocket = 0;

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, port);

    bzero(&sa, sizeof(struct sockaddr_in));

    gethostname(hostname, MAX_HOSTNAME);
    hp = gethostbyname(hostname);
    if (hp == NULL) {
	log_fatal("gethostbyname");
	proper_exit(MUD_HALT);
    }
    sa.sin_family = hp->h_addrtype;
    sa.sin_port = htons(port);
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
	log_fatal("Init-socket");
	proper_exit(MUD_HALT);
    }
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int *)&opt, sizeof(opt)) < 0) {
	log_fatal("setsockopt REUSEADDR");
	proper_exit(MUD_HALT);
    }

    for (i = 60; i > 0; i--) {
	if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
	    gotsocket = 0;
	    log_info("Socket in use... retrying...\n");
	    sleep(2);
	} else {
	    gotsocket = 1;
	    break;
	}
    }
    if (!gotsocket) {
	close(s);
	log_fatal("bind");
	proper_exit(MUD_HALT);
    }
    listen(s, 3);
    return (s);
}

int new_connection(int s)
{
    struct sockaddr_in                      isa;
    socklen_t                               i = 0;
    int                                     t = 0;

    if (DEBUG > 1)
	log_info("called %s with %d", __PRETTY_FUNCTION__, s);

    i = (socklen_t) sizeof(isa);
    getsockname(s, (struct sockaddr *)&isa, &i);
    if ((t = accept(s, (struct sockaddr *)&isa, &i)) < 0) {
	log_error("Accept");
	return (-1);
    }
    nonblock(t);
    return t;
}

int new_descriptor(int s)
{
    struct sockaddr_in                      isa;
    struct hostent                         *host = NULL;
    struct descriptor_data                 *newd = NULL;
    time_t                                  tc = (time_t) 0;
    struct tm                              *t_info = NULL;
    long                                    remote_addr = 0L;
    socklen_t                               i = 0;

    /*
     * int remote_port = 0; 
     */
    int                                     desc = 0;
    int                                     old_maxdesc = 0;
    int                                     desc_index = 0;

#ifdef RFC1413
    int                                     badger = 0;
#endif

    const char                             *timed_con[] = {
	"\n"
    };

/*
  const char                             *bannished[] = {
    "\n"
  };
*/

    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, s);

    tc = time(0);
    t_info = localtime(&tc);

    old_maxdesc = maxdesc;

    i = (socklen_t) sizeof(isa);
    getsockname(s, (struct sockaddr *)&isa, &i);
    if ((desc = accept(s, (struct sockaddr *)&isa, &i)) < 0) {
	log_error("Accept");
	return (-1);
    }
    nonblock(desc);

    if ((maxdesc + 1) >= avail_descs) {
	write_to_descriptor(desc, "Sorry.. full...\r\n");
	close(desc);
	return (0);
    } else if (desc > maxdesc)
	maxdesc = desc;

    CREATE(newd, struct descriptor_data, 1);

    /*
     * remote_port = ntohs(isa.sin_port); 
     */
    remote_addr = htonl(isa.sin_addr.s_addr);

    newd->username[0] = '\0';
#ifdef RFC1413
    {
	char                                   *ack = NULL;

	if ((ack = ident_id(desc, 10)))
	    strncpy(newd->username, ack, 16);
	else
	    badger = 2;
    }
#endif
    if (!newd->username[0])
	strcpy(newd->username, "adork");

    sprintf(newd->ip, "%lu.%lu.%lu.%lu",
	    (remote_addr & 0xff000000) >> 24,
	    (remote_addr & 0x00ff0000) >> 16,
	    (remote_addr & 0x0000ff00) >> 8, (remote_addr & 0x000000ff) >> 0);

    if ((host = gethostbyaddr((char *)&isa.sin_addr, sizeof(isa.sin_addr), AF_INET)))
	strncpy(newd->host, host->h_name, 49);

    /*
     * init desc data 
     */
    newd->descriptor = desc;
    newd->connected = 1;
    newd->wait = 1;
    newd->prompt_mode = 0;
    *newd->buf = '\0';
    newd->str = 0;
    newd->showstr_head = 0;
    newd->showstr_point = 0;
    newd->page_first = NULL;
    newd->page_last = NULL;
    newd->page_control = ' ';				       /* show the first page! */
    *newd->last_input = '\0';
    newd->output.head = NULL;
    newd->input.head = NULL;
    newd->next = descriptor_list;
    newd->character = 0;
    newd->original = 0;
    newd->snoop.snooping = 0;
    newd->snoop.snoop_by = 0;

#ifdef RFC1413
    switch (badger) {
	case 1:
	    write_to_descriptor(desc,
				"\r\n **** Tell your sys-admin to upgrade to the SHINY NEW telnet using RFC1413 ****\r\n\r\n");
	    break;
	case 2:
	    write_to_descriptor(desc,
				"\r\n **** Tell your sys-admin to practice safe telnet by using RFC1413 ****\r\n\r\n");
	    break;
	default:
	    break;
    }
#endif

    /*
     * prepend to list 
     */

    if (((t_info->tm_hour + 1) > 8) && ((t_info->tm_hour + 1) < 21))
	for (desc_index = 0; strcmp(timed_con[desc_index], "\n"); desc_index++) {
	    if (!strncmp(timed_con[desc_index], newd->ip, 19)) {
		log_info("TIMED site connecting:%s\n", newd->ip);
		dcprintf(newd, "\r\nThis site is blocked from : 9 am - 9 pm\r\n");
		dcprintf(newd, "You may connect after 9 pm from :[%s]\r\n", newd->ip);
		maxdesc = old_maxdesc;
		DESTROY(newd);
		close(desc);
		return (0);
	    }
	}
    /*
     * for (desc_index = 0; bannished[desc_index] != "\n"; desc_index++) { 
     */
    /*
     * if (!strncmp(bannished[desc_index], newd->ip, 19)) 
     */
    if (banned_ip(newd->ip)) {
	log_info("BAN site connecting:%s\n", newd->ip);
	dcprintf(newd, "\r\nDue to your System Administrators request, or for some\r\n");
	dcprintf(newd, "other reason, we are refusing all connections from:[%s]\r\n", newd->ip);
	maxdesc = old_maxdesc;
	DESTROY(newd);
	close(desc);
	return (0);
    }
    /*
     * } 
     */

    descriptor_list = newd;
    SEND_TO_Q(greetings, newd);
    SEND_TO_Q("By what name do you wish to be known? ", newd);
    return (0);
}

int process_output(struct descriptor_data *t)
{
    char                                    i[MAX_STRING_LENGTH + 1] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) t);

    if (!t->prompt_mode && !t->connected)
	if (write_to_descriptor(t->descriptor, "\r\n") < 0)
	    return (-1);

    /*
     * Cycle thru output queue 
     */
    while (get_from_q(&t->output, i)) {
	if ((t->snoop.snoop_by) && (t->snoop.snoop_by->desc)) {
	    write_to_q("S* ", &t->snoop.snoop_by->desc->output, 1);
	    write_to_q(i, &t->snoop.snoop_by->desc->output, 0);
	}
	if (write_to_descriptor(t->descriptor, i))
	    return (-1);
    }

    if (!t->connected && !(t->character && !IS_NPC(t->character) &&
			   IS_SET(t->character->specials.act, PLR_COMPACT)))
	if (write_to_descriptor(t->descriptor, "\r\n") < 0)
	    return (-1);

    return (1);
}

int write_to_descriptor(int desc, const char *txt)
{
    int                                     sofar = 0;
    int                                     thisround = 0;
    int                                     total = 0;

    if (DEBUG > 2)
	log_info("called %s with %d, %s", __PRETTY_FUNCTION__, desc, VNULL(txt));

    total = strlen(txt);

    do {
	thisround = write(desc, txt + sofar, total - sofar);
	if (thisround < 0) {
	    if (errno == EWOULDBLOCK)
		break;
	    log_error("Write to socket");
	    return (-1);
	}
	sofar += thisround;
    }
    while (sofar < total);

    return (0);
}

int process_input(struct descriptor_data *t)
{
    int                                     sofar = 0;
    int                                     thisround = 0;
    int                                     begin = 0;
    int                                     squelch = 0;
    int                                     i = 0;
    int                                     k = 0;
    int                                     flag = FALSE;
    long                                    now_time = 0L;
    char                                    tmp[MAX_INPUT_LENGTH + 2] = "\0\0\0\0\0\0\0";
    char                                    buffer[MAX_INPUT_LENGTH + 60] = "\0\0\0\0\0\0\0";

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) t);

    begin = strlen(t->buf);

    /*
     * Read in some stuff 
     */
    do {
	if ((thisround = read(t->descriptor, t->buf + begin + sofar,
			      MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0) {
	    sofar += thisround;
	} else {
	    if (thisround < 0) {
		if (errno != EWOULDBLOCK) {
		    log_error("Read1 - ERROR");
		    return (-1);
		} else {
		    break;
		}
	    } else {
		log_info("EOF encountered on socket read.");
		return (-1);
	    }
	}
    } while (!ISNEWL(*(t->buf + begin + sofar - 1)));

    *(t->buf + begin + sofar) = 0;

    /*
     * if no newline is contained in input, return without proc'ing 
     */
    for (i = begin; !ISNEWL(*(t->buf + i)); i++)
	if (!*(t->buf + i))
	    return (0);

    /*
     * input contains 1 or more newlines; process the stuff 
     */
    for (i = 0, k = 0; *(t->buf + i);) {
	if (!ISNEWL(*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2))))
	    if (*(t->buf + i) == '\b') {		       /* backspace */
		if (k) {				       /* more than one char ? */
		    if (*(tmp + --k) == '$')
			k--;
		    i++;
		} else {
		    i++;				       /* no or just one char.. Skip backsp */
		}
	    } else {
		if (isascii(*(t->buf + i)) && isprint(*(t->buf + i))) {
		    /*
		     * trans char, double for '$' (printf)        
		     */
		    if ((*(tmp + k) = *(t->buf + i)) == '$')
			*(tmp + ++k) = '$';
		    k++;
		    i++;
		} else {
		    i++;
		}
	} else {
	    *(tmp + k) = 0;
	    if (*tmp == '!')
		strcpy(tmp, t->last_input);
	    else
		strcpy(t->last_input, tmp);

	    write_to_q(tmp, &t->input, 0);

	    now_time = time(0);
	    t->idle_time = now_time;
	    if ((t->snoop.snoop_by) && (t->snoop.snoop_by->desc)) {
		write_to_q("% ", &t->snoop.snoop_by->desc->output, 1);
		write_to_q(tmp, &t->snoop.snoop_by->desc->output, 0);
		write_to_q("\r\n", &t->snoop.snoop_by->desc->output, 0);
	    }
	    if (flag) {
		sprintf(buffer, "Line too long. Truncated to:\r\n%s\r\n", tmp);
		if (write_to_descriptor(t->descriptor, buffer) < 0)
		    return (-1);

		/*
		 * skip the rest of the line 
		 */
		for (; !ISNEWL(*(t->buf + i)); i++);
	    }
	    /*
	     * find end of entry 
	     */
	    for (; ISNEWL(*(t->buf + i)); i++);

	    /*
	     * squelch the entry from the buffer 
	     */
	    for (squelch = 0;; squelch++)
		if ((*(t->buf + squelch) = *(t->buf + i + squelch)) == '\0')
		    break;
	    k = 0;
	    i = 0;
	}
    }
    return (1);
}

void close_sockets(int s)
{
    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, s);

    log_info("Closing all sockets.");
    while (descriptor_list)
	close_socket(descriptor_list);
    close(s);
}

void close_socket(struct descriptor_data *d)
{
    struct descriptor_data                 *tmp = NULL;

    if (DEBUG > 2)
	log_info("called %s with %08zx", __PRETTY_FUNCTION__, (size_t) d);

    if (!d)
	return;

    close(d->descriptor);
    flush_queues(d);
    if (d->descriptor == maxdesc)
	--maxdesc;

    /*
     * Forget snooping 
     */

    if (d->snoop.snooping)
	d->snoop.snooping->desc->snoop.snoop_by = 0;

    if (d->snoop.snoop_by) {
	cprintf(d->snoop.snoop_by, "Your victim is no longer among us.\r\n");
	d->snoop.snoop_by->desc->snoop.snooping = 0;
    }
    if (d->character)
	if (d->connected == CON_PLAYING) {
	    do_save(d->character, "", 0);
	    act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
	    /*
	     * log_info("Closing link to: %s.", GET_NAME(d->character)); 
	     */
	    log_auth(d->character, "LINKDEAD %s (%s@%s/%s)!", GET_NAME(d->character),
		     d->username, d->host, d->ip);
	    if (IS_NPC(d->character)) {
		if (d->character->desc)
		    d->character->orig = d->character->desc->original;
	    }
	    d->character->desc = NULL;
	} else {
	    if (GET_NAME(d->character)) {
		/*
		 * log_info("Losing player: %s.", GET_NAME(d->character)); 
		 */
		log_auth(d->character, "GOODBYE %s (%s@%s/%s)!", GET_NAME(d->character),
			 d->username, d->host, d->ip);
	    }
	    free_char(d->character);
	    d->character = NULL;			       /* need to wipe this out so we don't pick at it! */
    } else
	log_info("Losing descriptor without char.");

    if (next_to_process == d)				       /* to avoid crashing the process loop */
	next_to_process = next_to_process->next;

    if (d == descriptor_list)				       /* this is the head of the list */
	descriptor_list = descriptor_list->next;
    else {						       /* This is somewhere inside the list */
	/*
	 * Locate the previous element 
	 */
	for (tmp = descriptor_list; (tmp->next != d) && tmp; tmp = tmp->next);

	tmp->next = d->next;
    }
    /*
     * if (d->showstr_head) 
     */
    DESTROY(d->showstr_head);
    DESTROY(d);
}

void nonblock(int s)
{
    if (DEBUG > 2)
	log_info("called %s with %d", __PRETTY_FUNCTION__, s);

    if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
	log_fatal("Noblock");
	proper_exit(MUD_HALT);
    }
}

/*
 * Public routines for system-to-player-communication
 */

/*
 * This acts as an interface to write_to_q(), but it uses variable arguments
 * to eliminate multiple calls to sprintf().
 */
void dcprintf(struct descriptor_data *d, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];

    if (Str && *Str && d) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	write_to_q(Result, &d->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %08zx, %s, result of %s", __PRETTY_FUNCTION__, (size_t) d,
		     VNULL(Str), Result);
    }
}

/*
 * This works like send_to_char(), but it uses variable arguments to
 * eliminate multiple calls to sprintf().
 */
void cprintf(struct char_data *ch, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];

    if (Str && *Str && ch && ch->desc) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	write_to_q(Result, &ch->desc->output, 1);
	/*
	 * Cannot call things in bug.c from things bug.c calls!
	 *
	 * if (DEBUG > 2)
	 *   log_info("called %s with %s, %s, result of %s", __PRETTY_FUNCTION__, SAFE_NAME(ch), VNULL(Str), Result);
	 */
    }
}

/*
 * This one is an interface to replace send_to_room().
 */
void rprintf(int room, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct char_data                       *i = NULL;
    struct room_data                       *rr = NULL;

    if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = rr->people; i; i = i->next_in_room)
	    if (i->desc)
		write_to_q(Result, &i->desc->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %d, %s, result of %s", __PRETTY_FUNCTION__, room,
		     VNULL(Str), Result);
    }
}

/*
 * This one is everyone in the zone specified.
 */
void zprintf(int zone, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct descriptor_data                 *i = NULL;
    struct room_data                       *rr = NULL;

    if (Str && *Str && zone >= 0) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected)
		if (i->character)
		    if ((rr = real_roomp(i->character->in_room)))
			if (rr->zone == zone)
			    write_to_q(Result, &i->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %d, %s, result of %s", __PRETTY_FUNCTION__, zone,
		     VNULL(Str), Result);
    }
}

/*
 * And this one sends to EVERYBODY int the game!!!!!
 */
void allprintf(const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct descriptor_data                 *i = NULL;

    if (Str && *Str) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected)
		write_to_q(Result, &i->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
		     Result);
    }
}

/*
 * Here is send_to_outdoor()
 */
void oprintf(const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct descriptor_data                 *i = NULL;

    if (Str && *Str) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected && i->character && OUTSIDE(i->character))
		write_to_q(Result, &i->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
		     Result);
    }
}

/*
 * Send to everyone except the given character.
 */
void eprintf(struct char_data *ch, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct descriptor_data                 *i = NULL;

    if (Str && *Str) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = descriptor_list; i; i = i->next)
	    if (ch && ch->desc != i && !i->connected)
		write_to_q(Result, &i->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %s, %s, result of %s", __PRETTY_FUNCTION__, SAFE_NAME(ch),
		     VNULL(Str), Result);
    }
}

/*
 * This one is for send_to_room_except()
 */
void reprintf(int room, struct char_data *ch, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct char_data                       *i = NULL;
    struct room_data                       *rr = NULL;

    if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = rr->people; i; i = i->next_in_room)
	    if (i != ch && i->desc)
		write_to_q(Result, &i->desc->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %d, %s, %s, result of %s", __PRETTY_FUNCTION__, room,
		     SAFE_NAME(ch), VNULL(Str), Result);
    }
}

/*
 * This one is for send_to_room_except()
 */
void re2printf(int room, struct char_data *ch1, struct char_data *ch2, const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct char_data                       *i = NULL;
    struct room_data                       *rr = NULL;

    if (Str && *Str && room >= 0 && (rr = real_roomp(room))) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = rr->people; i; i = i->next_in_room)
	    if (i != ch1 && i != ch2 && i->desc)
		write_to_q(Result, &i->desc->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %d, %s, %s, %s, result of %s", __PRETTY_FUNCTION__, room,
		     SAFE_NAME(ch1), SAFE_NAME(ch2), VNULL(Str), Result);
    }
}

/*
 * IMMORTAL printf.
 */
void iprintf(const char *Str, ...)
{
    va_list                                 arg;
    char                                    Result[MAX_STRING_LENGTH];
    struct descriptor_data                 *i = NULL;

    if (Str && *Str) {
	bzero(Result, MAX_STRING_LENGTH);
	va_start(arg, Str);
	vsprintf(Result, Str, arg);
	va_end(arg);
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected && i->character && IS_IMMORTAL(i->character))
		write_to_q(Result, &i->output, 1);
	if (DEBUG > 2)
	    log_info("called %s with %s, result of %s", __PRETTY_FUNCTION__, VNULL(Str),
		     Result);
    }
}

void save_all()
{
    struct descriptor_data                 *i = NULL;

    if (DEBUG > 2)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    for (i = descriptor_list; i; i = i->next)
	if (i->character)
	    save_char(i->character, NOWHERE);
}

/* higher-level communication */

void act(const char *Str, int hide_invisible, struct char_data *ch,
	 struct obj_data *obj, void *vict_obj, int type, ...)
{
    char                                   *strp = NULL;
    char                                   *point = NULL;
    const char                             *i = NULL;
    struct char_data                       *to = NULL;
    char                                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                                    str[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    va_list                                 arg;

    if (DEBUG > 2)
	log_info("called %s with %s, %d, %s, %08zx, %08zx, %d", __PRETTY_FUNCTION__, VNULL(Str),
		 hide_invisible, SAFE_NAME(ch), (size_t) obj, (size_t) vict_obj, type);

    if (!Str)
	return;
    if (!*Str)
	return;

    bzero(buf, MAX_STRING_LENGTH);
    bzero(str, MAX_STRING_LENGTH);

    va_start(arg, type);
    vsprintf(str, Str, arg);
    va_end(arg);

    if (DEBUG > 1) {
	log_info("act got: %s", Str);
	log_info("act became: %s", str);
    }

    /*
     * Added checks to ensure ch and to are NOT NULL 
     */
    if (type == TO_VICT) {
	to = (struct char_data *)vict_obj;
	if (!to || !ch)
	    return;
    } else if (type == TO_CHAR) {
	to = ch;
	if (!to)
	    return;
    } else {
	if (!ch)
	    return;
	if (!real_roomp(ch->in_room))
	    return;
	if (!(to = real_roomp(ch->in_room)->people))
	    return;
    }

    for (; to; to = to->next_in_room) {
	if (to->desc && ((to != ch) || (type == TO_CHAR)) &&
	    (CAN_SEE(to, ch) || !hide_invisible) && AWAKE(to) &&
	    !((type == TO_NOTVICT) && (to == (struct char_data *)vict_obj))) {
	    for (strp = str, point = buf;;)
		if (*strp == '$') {
		    switch (*(++strp)) {
			case 'n':
			    i = PERS(ch, to);
			    break;
			case 'N':
			    i = PERS((struct char_data *)vict_obj, to);
			    break;
			case 'm':
			    i = HMHR(ch);
			    break;
			case 'M':
			    i = HMHR((struct char_data *)vict_obj);
			    break;
			case 's':
			    i = HSHR(ch);
			    break;
			case 'S':
			    i = HSHR((struct char_data *)vict_obj);
			    break;
			case 'e':
			    i = HSSH(ch);
			    break;
			case 'E':
			    i = HSSH((struct char_data *)vict_obj);
			    break;
			case 'o':
			    i = OBJN(obj, to);
			    break;
			case 'O':
			    i = OBJN((struct obj_data *)vict_obj, to);
			    break;
			case 'p':
			    i = OBJS(obj, to);
			    break;
			case 'P':
			    i = OBJS((struct obj_data *)vict_obj, to);
			    break;
			case 'a':
			    i = SANA(obj);
			    break;
			case 'A':
			    i = SANA((struct obj_data *)vict_obj);
			    break;
			case 'T':
			    i = (char *)vict_obj;
			    break;
			case 'F':
			    i = fname((char *)vict_obj);
			    break;
			case '$':
			    i = "$";
			    break;
			default:
			    log_info("Illegal $-code to act(): %s", str);
			    break;
		    }

		    while ((*point = *(i++)))
			++point;

		    ++strp;

		} else if (!(*(point++) = *(strp++)))
		    break;

	    *(--point) = '\n';
	    *(++point) = '\r';
	    *(++point) = '\0';

	    write_to_q(CAP(buf), &to->desc->output, 1);
	    if (DEBUG > 1)
		log_info("act sent: %s", buf);
	}
	if ((type == TO_VICT) || (type == TO_CHAR))
	    return;
    }
}

void dump_player_list(void)
{
    FILE                                   *pfd = NULL;
    int                                     i = 0;

    if (DEBUG > 2)
	log_info("called %s with no arguments", __PRETTY_FUNCTION__);

    log_info("Dumping player list");
    if (!(pfd = fopen(PLAYER_FILE, "w"))) {
	log_error("Cannot save player data for new user!");
    } else {
	fprintf(pfd, "%d\n", actual_players);
	for (i = 0; i < number_of_players; i++)
	    if (list_of_players[i])
		fprintf(pfd, "%s", list_of_players[i]);
	FCLOSE(pfd);
    }
}

void proper_exit(int exit_code)
{
#if 0
    log_boot("Disconnecting from database!");
    close_sql();
#endif
    exit(exit_code);
}