AwakeMUD-0.84B/.ssh/
AwakeMUD-0.84B/doc/
AwakeMUD-0.84B/lib/
AwakeMUD-0.84B/lib/etc/pfiles/
AwakeMUD-0.84B/lib/fixer_data/
AwakeMUD-0.84B/lib/misc/
AwakeMUD-0.84B/lib/text/
AwakeMUD-0.84B/lib/text/help/
AwakeMUD-0.84B/lib/text/wizhelp/
AwakeMUD-0.84B/lib/veh/
AwakeMUD-0.84B/lib/world/
AwakeMUD-0.84B/lib/world/mob/
AwakeMUD-0.84B/lib/world/mtx/
AwakeMUD-0.84B/lib/world/qst/
AwakeMUD-0.84B/lib/world/shp/
AwakeMUD-0.84B/lib/world/veh/
/* ************************************************************************
*   File: comm.c                                        Part of CircleMUD *
*  Usage: Communication, socket handling, main(), central game loop       *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#define __COMM_CC__

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <new>
#include <time.h>
#include <iostream>
using namespace std;

#if defined(WIN32) && !defined(__CYGWIN__)
#include <windows.h>
#include <winsock.h>
#include <io.h>
#include <direct.h>
#include <mmsystem.h>
typedef SOCKET  socket_t;

#define chdir(x) _chdir(x)
#define random() rand()
#define srandom(x) srand(x)
#define close(x) _close(x)
#define write(x, y, z) _write(x, y, z)
#define read(x, y, z) _read(x, y, z)
#else
#include <netinet/in.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netdb.h>
#endif

#include "telnet.h"
#include "awake.h"
#include "structs.h"
#include "utils.h"
#include "constants.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "newdb.h"
#include "house.h"
#include "memory.h"
#include "screen.h"
#include "spells.h"
#include "shop.h"
#include "quest.h"
#include "newmatrix.h"

/* externs */
extern int restrict;
/* extern FILE *player_fl; */
extern int DFLT_PORT;
extern char *DFLT_DIR;
extern int MAX_PLAYERS;
extern int MAX_DESCRIPTORS_AVAILABLE;

extern struct time_info_data time_info; /* In db.c */
extern char help[];

/* local globals */
struct descriptor_data *descriptor_list = NULL; /* master desc list */
struct txt_block *bufpool = 0;  /* pool of large output buffers */
int buf_largecount = 0;         /* # of large buffers which exist */
int buf_overflows = 0;          /* # of overflows of output */
int buf_switches = 0;           /* # of switches from small to large buf */
int circle_shutdown = 0;        /* clean shutdown */
static int exit_code = SUCCESS;
int circle_reboot = 0;          /* reboot the game after a shutdown */
int no_specials = 0;            /* Suppress ass. of special routines */
int max_players = 0;            /* max descriptors available */
int tics = 0;                   /* for extern checkpointing */
int scheck = 0;                 /* for syntax checking mode */
extern int nameserver_is_slow;  /* see config.c */
struct timeval zero_time;       // zero-valued time structure, used to be
// called 'null_time' but that is kinda
// innacurate (=

static bool fCopyOver;
int  mother_desc;
int port;

int ViolencePulse = PULSE_VIOLENCE;

/* functions in this file */
int get_from_q(struct txt_q * queue, char *dest, int *aliased);
void init_game(int port);
void signal_setup(void);
void game_loop(int mother_desc);
int init_socket(int port);
int new_descriptor(int s);
int get_max_players(void);
int process_output(struct descriptor_data * t);
int process_input(struct descriptor_data * t);
void close_socket(struct descriptor_data * d);
struct timeval timediff(struct timeval * a, struct timeval * b);
void flush_queues(struct descriptor_data * d);
void nonblock(int s);
int perform_subst(struct descriptor_data * t, char *orig, char *subst);
int perform_alias(struct descriptor_data * d, char *orig);
void record_usage(void);
void make_prompt(struct descriptor_data * point);
void check_idle_passwords(void);
void init_descriptor (struct descriptor_data *newd, int desc);
char *colorize(struct descriptor_data *d, char *str);

/* extern fcnts */
extern void DBInit();
extern void DBEnd();
void boot_world(void);
void zone_update(void);
void spec_update(void);
void affect_update(void);       /* In spells.c */
void point_update(void);        /* In limits.c */
void mobile_activity(void);
void string_add(struct descriptor_data * d, char *str);
void perform_violence(void);
void order_list(...);
void misc_update(void);
void decide_combat_pool(void);
int isbanned(char *hostname);
void matrix_violence(void);
void matrix_update(void);
void another_minute(void);
void weather_change(void);
void process_regeneration(int half_hour);
void MonorailProcess();
void EscalatorProcess();
void ElevatorProcess();
void taxi_leaves(void);
void johnson_update();
void check_idling(void);
void free_shop(struct shop_data *shop);
void free_quest(struct quest_data *quest);
void randomize_shop_prices(void);
void process_autonav(void);
void update_programs(void);
void process_boost(void);
class memoryClass *Mem = new memoryClass();

#if defined(WIN32) && !defined(__CYGWIN__)
void gettimeofday(struct timeval *t, struct timezone *dummy)
{
  DWORD millisec = GetTickCount();

  t->tv_sec = (int) (millisec / 1000);
  t->tv_usec = (millisec % 1000) * 1000;
}
#endif

/* *********************************************************************
*  main game loop and related stuff                                    *
********************************************************************* */

int main(int argc, char **argv)
{
  int pos = 1;
  char *dir;
  port = DFLT_PORT;
  dir = DFLT_DIR;
  while ((pos < argc) && (*(argv[pos]) == '-')) {
    switch (*(argv[pos] + 1)) {
    case 'd':
      if (*(argv[pos] + 2))
        dir = argv[pos] + 2;
      else if (++pos < argc)
        dir = argv[pos];
      else {
        log("Directory arg expected after option -d.");
        exit(1);
      }
      break;
    case 'c':
      scheck = 1;
      log("Syntax check mode enabled.");
      break;
    case 'r':
      restrict = 1;
      log("Restricting game -- no new players allowed.");
      break;
    case 'o':
      fCopyOver = TRUE;
      mother_desc = atoi(argv[pos]+2);
      break;
    case 's':
      no_specials = 1;
      log("Suppressing assignment of special routines.");
      break;
    default:
      log("SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1));
      break;
    }
    pos++;
  }

  if (pos < argc) {
    if (!isdigit(*argv[pos])) {
      fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n", argv[0]);
      exit(1);
    } else if ((port = atoi(argv[pos])) <= 1024) {
      fprintf(stderr, "Illegal port number.\n");
      exit(1);
    }
  }

  if (chdir(dir) < 0) {
    perror("Fatal error changing to data directory");
    exit(1);
  }

  log("Using %s as data directory.", dir);

  if (scheck) {
    boot_world();
    log("Done.");
    exit(0);
  } else {
    log("Running game on port %d.", port);
    init_game(port);
  }

  return 0;
}
/* Reload players after a copyover */

void copyover_recover()
{
  struct descriptor_data *d;
  FILE *fp;
  char host[1024];
  bool fOld;
  char name[MAX_INPUT_LENGTH];
  int desc;

  log ("Copyover recovery initiated");

  fp = fopen (COPYOVER_FILE, "r");

  if (!fp) // there are some descriptors open which will hang forever then?

  {
    perror ("copyover_recover:fopen");
    log ("Copyover file not found. Exitting.\n\r");
    exit (1);
  }

  unlink (COPYOVER_FILE); // In case something crashes - doesn't prevent reading

  for (;;) {
    fOld = TRUE;
    fscanf (fp, "%d %s %s\n", &desc, name, host);
    if (desc == -1)
      break;

    /* Write something, and check if it goes error-free */
    if (write_to_descriptor (desc, "\n\rRestoring from copyover...\n\r") < 0) {
      close (desc); /* nope */
      continue;
    }

    /* create a new descriptor */
    CREATE (d, struct descriptor_data, 1);
    memset ((char *) d, 0, sizeof (struct descriptor_data));
    init_descriptor (d,desc); /* set up various stuff */

    strcpy(d->host, host);
    d->next = descriptor_list;
    descriptor_list = d;


    d->connected = CON_CLOSE;

    /* Now, find the pfile */

    CREATE(d->character, struct char_data, 1);
    clear_char(d->character);
    CREATE(d->character->player_specials, struct player_special_data, 1);
    d->character->desc = d;

    if ((d->character = playerDB.LoadChar(name, FALSE))) {
      d->character->desc = d;
      if (!PLR_FLAGGED(d->character, PLR_DELETED))
        PLR_FLAGS(d->character).RemoveBits(PLR_WRITING, PLR_MAILING, ENDBIT);
      else
        fOld = FALSE;
    } else
      fOld = FALSE;

    if (!fOld) /* Player file not found?! */
    {
      write_to_descriptor (desc, "\n\rSomehow, your character was lost in the copyover. Sorry.\n\r");
      close_socket (d);
    } else /* ok! */
    {
      long load_room;
      write_to_descriptor (desc, "\n\rCopyover recovery complete.\n\r");
      d->connected = CON_PLAYING;
      reset_char(d->character);
      d->character->next = character_list;
      character_list = d->character;
      if (real_room(GET_LAST_IN(d->character)) > 0)
        load_room = real_room(GET_LAST_IN(d->character));
      else
        load_room = real_room(GET_LOADROOM(d->character));
      char_to_room(d->character, load_room);
      look_at_room(d->character, 0);
    }
  }
  fclose (fp);

}


/* Init sockets, run game, and cleanup sockets */
void init_game(int port)
{
  srandom(time(0));
  //  log("Opening mother connection.");
  //  mother_desc = init_socket(port);

  max_players = get_max_players();
  if (!fCopyOver) /* If copyover mother_desc is already set up */
  {
    log ("Opening mother connection.");
    mother_desc = init_socket (port);
  }

  DBInit();

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

  if (fCopyOver) /* reload players */
    copyover_recover();

  log("Entering game loop.");
  game_loop(mother_desc);

  log("Saving all players.");
  House_save_all();

  DBEnd();

  log("Closing all sockets.");
  while (descriptor_list)
    close_socket(descriptor_list);

  close(mother_desc);

  if (circle_reboot) {
    log("Rebooting.");
    exit(52);                   /* what's so great about HHGTTG, anyhow? */
  }
  log("Normal termination of game.");
}

/*
 * init_socket sets up the mother descriptor - creates the socket, sets
 * its options up, binds it, and listens.
 */
int init_socket(int port)
{
  int s, opt;
  struct sockaddr_in sa;

  /*
   * Should the first argument to socket() be AF_INET or PF_INET?  I don't
   * know, take your pick.  PF_INET seems to be more widely adopted, and
   * Comer (_Internetworking with TCP/IP_) even makes a point to say that
   * people erroneously use AF_INET with socket() when they should be using
   * PF_INET.  However, the man pages of some systems indicate that AF_INET
   * is correct; some such as ConvexOS even say that you can use either one.
   * All implementations I've seen define AF_INET and PF_INET to be the same
   * number anyway, so ths point is (hopefully) moot.
   */

  if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
    perror("Create socket");
    exit(1);
  }
#if defined(SO_SNDBUF)
  opt = LARGE_BUFSIZE + GARBAGE_SPACE;
  if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) {
    perror("setsockopt SNDBUF");
    exit(1);
  }
#endif

#if defined(SO_REUSEADDR)
  opt = 1;
  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) {
    perror("setsockopt REUSEADDR");
    exit(1);
  }
#endif

#if defined(SO_REUSEPORT)
  opt = 1;
  if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (char *) &opt, sizeof(opt)) < 0) {
    perror("setsockopt REUSEPORT");
    exit(1);
  }
#endif

#if defined(SO_LINGER)
  {
    struct linger ld;

    ld.l_onoff = 0;
    ld.l_linger = 0;
    if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&ld, sizeof(ld)) < 0) {
      perror("setsockopt LINGER");
      exit(1);
    }
  }
#endif

  sa.sin_family = AF_INET;
  sa.sin_port = htons(port);
  sa.sin_addr.s_addr = htonl(INADDR_ANY);

  if (bind(s, (struct sockaddr *) & sa, sizeof(sa)) < 0) {
    perror("bind");
    close(s);
    exit(1);
  }
  nonblock(s);
  listen(s, 5);
  return s;
}

int get_max_players(void)
{
  int max_descs = 0;
  char *method;

  /*
   * First, we'll try using getrlimit/setrlimit.  This will probably work
   * on most systems.
   */
#if defined (RLIMIT_NOFILE) || defined (RLIMIT_OFILE)
#if !defined(RLIMIT_NOFILE)
#define RLIMIT_NOFILE RLIMIT_OFILE
#endif

  {
    struct rlimit limit;

    method = "rlimit";
    if (getrlimit(RLIMIT_NOFILE, &limit) < 0)
    {
      perror("calling getrlimit");
      exit(1);
    }
    if (limit.rlim_max == RLIM_INFINITY)
      max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS;
    else
      max_descs = MIN((unsigned)MAX_PLAYERS + NUM_RESERVED_DESCS, (unsigned)limit.rlim_max);

    limit.rlim_cur = max_descs;
    setrlimit(RLIMIT_NOFILE, &limit);
  }
#elif defined (OPEN_MAX) || defined(FOPEN_MAX)
#if !defined(OPEN_MAX)
#define OPEN_MAX FOPEN_MAX
#endif
  method = "OPEN_MAX";
  max_descs = OPEN_MAX;         /* Uh oh.. rlimit didn't work, but we have
  * OPEN_MAX */
#else
  /*
  * Okay, you don't have getrlimit() and you don't have OPEN_MAX.  Time to
  * use the POSIX sysconf() function.  (See Stevens' _Advanced Programming
  * in the UNIX Environment_).
  */
  method = "POSIX sysconf";
  errno = 0;
  if ((max_descs = sysconf(_SC_OPEN_MAX)) < 0) {
    if (errno == 0)
      max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS;
    else {
      perror("Error calling sysconf");
      exit(1);
    }
  }
#endif

  /* now calculate max _players_ based on max descs */
  max_descs = MIN(MAX_PLAYERS, max_descs - NUM_RESERVED_DESCS);

  if (max_descs <= 0) {
    log("Non-positive max player limit!  (Set at %d using %s).",
        max_descs, method);

    exit(1);
  }

  log("Setting player limit to %d using %s.", max_descs, method);

  return max_descs;
}

/*
 * game_loop contains the main loop which drives the entire MUD.  It
 * cycles once every 0.10 seconds and is responsible for accepting new
 * new connections, polling existing connections for input, dequeueing
 * output and sending it out to players, and calling "heartbeat" functions
 * such as mobile_activity().
 */
void game_loop(int mother_desc)
{
  fd_set input_set, output_set, exc_set;
  struct timeval last_time, now, timespent, timeout, opt_time;
  char comm[MAX_INPUT_LENGTH];
  struct descriptor_data *d, *next_d;
  int pulse = 0, maxdesc, aliased;
  int i;

  /* initialize various time values */
  zero_time.tv_sec = 0;
  zero_time.tv_usec = 0;
  opt_time.tv_usec = OPT_USEC;
  opt_time.tv_sec = 0;
  gettimeofday(&last_time, (struct timezone *) 0);

  /* The Main Loop.  The Big Cheese.  The Top Dog.  The Head Honcho.  The.. */
  while (!circle_shutdown) {

    /* Sleep if we don't have any connections */
    if (descriptor_list == NULL) {
      FD_ZERO(&input_set);
      FD_SET(mother_desc, &input_set);
      if ((select(mother_desc + 1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0)) {
        if (errno == EINTR)
          log("Waking up to process signal.");
        else
          perror("Select coma");
      } // else log("New connection.  Waking up.");
      gettimeofday(&last_time, (struct timezone *) 0);
    }
    /* Set up the input, output, and exception sets for select(). */
    FD_ZERO(&input_set);
    FD_ZERO(&output_set);
    FD_ZERO(&exc_set);
    FD_SET(mother_desc, &input_set);
    maxdesc = mother_desc;
    for (d = descriptor_list; d; d = d->next) {
      if (d->descriptor > maxdesc)
        maxdesc = d->descriptor;
      FD_SET(d->descriptor, &input_set);
      FD_SET(d->descriptor, &output_set);
      FD_SET(d->descriptor, &exc_set);
    }

    /*
     * At this point, the original Diku code set up a signal mask to avoid
     * block all signals from being delivered.  I believe this was done in
     * order to prevent the MUD from dying with an "interrupted system call"
     * error in the event that a signal be received while the MUD is dormant.
     * However, I think it is easier to check for an EINTR error return from
     * this select() call rather than to block and unblock signals.
     */
    do {
      errno = 0;                /* clear error condition */

      /* figure out for how long we have to sleep */
      gettimeofday(&now, (struct timezone *) 0);
      timespent = timediff(&now, &last_time);
      timeout = timediff(&opt_time, &timespent);

      /* sleep until the next 0.1 second mark */
      if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0)
        if (errno != EINTR) {
          perror("Select sleep");
          exit(1);
        }
    } while (errno);

    /* record the time for the next pass */
    gettimeofday(&last_time, (struct timezone *) 0);

    // I added this for Linux, because &zero_time gets modified to become
    // time not slept.  This could possibly change the amount of time the
    // mud decides to sit on select(), not a good thing being that it should
    // return immediately (zero_time = 0).
    zero_time.tv_sec = 0;
    zero_time.tv_usec = 0;
    /* poll (without blocking) for new input, output, and exceptions */
    if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &zero_time)<0) {
      perror("Select poll");
      return;
    }
    /* New connection waiting for us? */
    if (FD_ISSET(mother_desc, &input_set))
      new_descriptor(mother_desc);

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

    /* process descriptors with input pending */
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;
      if (FD_ISSET(d->descriptor, &input_set))
        if (process_input(d) < 0)
          close_socket(d);
    }

    /* process descriptors with ident pending */
    /*
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;

      if (waiting_for_ident(d))
       ident_check(d, pulse);
    }
    */

    /* process commands we just read from process_input */
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;

      if ((--(d->wait) <= 0) && get_from_q(&d->input, comm, &aliased)) {
        if (d->character) {
          d->character->char_specials.timer = 0;
          if (d->original)
            d->original->char_specials.timer = 0;
          if (!d->connected && GET_WAS_IN(d->character) != NOWHERE) {
            if (d->character->in_room != NOWHERE)
              char_from_room(d->character);
            char_to_room(d->character, GET_WAS_IN(d->character));
            GET_WAS_IN(d->character) = NOWHERE;
            act("$n has returned.", TRUE, d->character, 0, 0, TO_ROOM);
          }
        }
        d->wait = 1;
        d->prompt_mode = 1;

        if (d->str)                     /* writing boards, mail, etc.   */
          string_add(d, comm);
        else if (d->connected != CON_PLAYING)   /* in menus, etc.       */
          nanny(d, comm);
        else {                          /* else: we're playing normally */
          if (aliased) /* to prevent recursive aliases */
            d->prompt_mode = 0;
          else {
            if (perform_alias(d, comm)) /* run it through aliasing system */
              get_from_q(&d->input, comm, &aliased);
          }
          command_interpreter(d->character, comm); /* send it to interpreter */
        }
      }
    }

    /* send queued output out to the operating system (ultimately to user) */
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;
      if (FD_ISSET(d->descriptor, &output_set) && *(d->output))
        if (process_output(d) < 0)
          close_socket(d);
        else
          d->prompt_mode = 1;
    }

    /* kick out folks in the CON_CLOSE state */
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;
      if (STATE(d) == CON_CLOSE)
        close_socket(d);
    }

    /* give each descriptor an appropriate prompt */
    for (d = descriptor_list; d; d = d->next) {
      if (d->prompt_mode) {
        make_prompt(d);
        d->prompt_mode = 0;
      }
    }

    /* handle heartbeat stuff */
    /* Note: pulse now changes every 0.10 seconds  */

    pulse++;

    if (!(pulse % PULSE_ZONE)) {
      zone_update();
      phone_check();
      process_autonav();
      process_boost();
    }

    if (!(pulse % PULSE_SPECIAL)) {
      spec_update();
    }

    if (!(pulse % PULSE_MONORAIL)) {
      MonorailProcess();
    }

    if (!(pulse % PULSE_MOBILE)) {
      mobile_activity();
    }

    if (!(pulse % ViolencePulse)) {
      EscalatorProcess();
      ElevatorProcess();
      order_list();
      decide_combat_pool();
      perform_violence();
      matrix_violence();
    }

    if (!(pulse % (SECS_PER_MUD_MINUTE * PASSES_PER_SEC))) {
      update_programs();
      another_minute();
      misc_update();
      matrix_update();
    }
    if (!(pulse % (5 * SECS_PER_MUD_MINUTE * PASSES_PER_SEC))) {
      if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC)))
        process_regeneration(1);
      else
        process_regeneration(0);
      taxi_leaves();
    }

    if (!(pulse % (24 * SECS_PER_MUD_HOUR * PASSES_PER_SEC)))
      randomize_shop_prices();

    if (!(pulse % (30 * SECS_PER_MUD_MINUTE * PASSES_PER_SEC)))
      affect_update();

    if (!(pulse % (60 * PASSES_PER_SEC))) {
      check_idling();
      // johnson_update();
    }

    if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) {
      // chars are saved in point_update
      point_update();
      weather_change();
      House_save_all();
      // Streetlight code
      if (time_info.hours == 17) {
        for (i = 0; i < top_of_world; i++) {
          if (ROOM_FLAGGED(i, ROOM_LIT)) {
            send_to_room("A streetlight hums faintly, flickers, and turns on.\r\n", i);
          }
        }
      }
      if (time_info.hours == 7) {
        for (i = 0; i < top_of_world; i++) {
          if (ROOM_FLAGGED(i, ROOM_LIT)) {
            send_to_room("A streetlight flickers and goes out.\r\n", i);
          }
        }
      }
    }

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


/* ******************************************************************
*  general utility stuff (for local use)                            *
****************************************************************** */

/*
 *  new code to calculate time differences, which works on systems
 *  for which tv_usec is unsigned (and thus comparisons for something
 *  being < 0 fail).  Based on code submitted by ss@sirocco.cup.hp.com.
 */

/*
 * code to return the time difference between a and b (a-b).
 * always returns a nonnegative value (floors at 0).
 */
struct timeval timediff(struct timeval * a, struct timeval * b)
{
  struct timeval rslt;

  if (a->tv_sec < b->tv_sec)
    return zero_time;
  else if (a->tv_sec == b->tv_sec)
  {
    if (a->tv_usec < b->tv_usec)
      return zero_time;
    else {
      rslt.tv_sec = 0;
      rslt.tv_usec = a->tv_usec - b->tv_usec;
      return rslt;
    }
  } else
  { /* a->tv_sec > b->tv_sec */
    rslt.tv_sec = a->tv_sec - b->tv_sec;
    if (a->tv_usec < b->tv_usec) {
      rslt.tv_usec = a->tv_usec + 1000000 - b->tv_usec;
      rslt.tv_sec--;
    } else
      rslt.tv_usec = a->tv_usec - b->tv_usec;
    return rslt;
  }
}

void record_usage(void)
{
  int sockets_connected = 0, sockets_playing = 0;
  struct descriptor_data *d;

  for (d = descriptor_list; d; d = d->next) {
    sockets_connected++;
    if (!d->connected)
      sockets_playing++;
  }

  log("usage: %-3d sockets connected, %-3d sockets playing",
      sockets_connected, sockets_playing);

#ifdef RUSAGE

  {
    struct rusage ru;

    getrusage(0, &ru);
    log("rusage: user time: %ld sec, system time: %ld sec, max res size: %ld",
        ru.ru_utime.tv_sec, ru.ru_stime.tv_sec, ru.ru_maxrss);
  }
#endif

}

/*
 * Turn off echoing (specific to telnet client)
 */
void echo_off(struct descriptor_data *d)
{
  char off_string[] =
    {
      (char) IAC,
      (char) WILL,
      (char) TELOPT_ECHO,
      (char) 0,
    };

  SEND_TO_Q(off_string, d);
}

/*
 * Turn on echoing (specific to telnet client)
 */
void echo_on(struct descriptor_data *d)
{
  char on_string[] =
    {
      (char) IAC,
      (char) WONT,
      (char) TELOPT_ECHO,
      (char) TELOPT_NAOFFD,
      (char) TELOPT_NAOCRD,
      (char) 0,
    };

  SEND_TO_Q(on_string, d);
}

void make_prompt(struct descriptor_data * d)
{
  char *prompt;

  if (d->str)
    write_to_descriptor(d->descriptor, "] ");
  else if (!d->connected)
  {
    struct char_data *ch;

    if (d->original) {
      prompt = GET_PROMPT(d->original);
      ch = d->original;
    } else {
      prompt = GET_PROMPT(d->character);
      ch = d->character;
    }

    if (!prompt || !*prompt)
      write_to_descriptor(d->descriptor, "> ");
    else if (!strchr(prompt, '@')) {
      prompt = colorize(d, prompt);
      write_to_descriptor(d->descriptor, prompt);
    } else {
      char temp[MAX_INPUT_LENGTH], str[11], *final;
      int i = 0, j;

      for (; *prompt; prompt++) {
        if (*prompt == '@' && *(prompt+1)) {
          prompt++;
          switch(*prompt) {
          case 'a':       // astral pool
            sprintf(str, "%d", GET_ASTRAL(d->character));
            break;
          case 'b':       // ballistic
            sprintf(str, "%d", GET_BALLISTIC(d->character));
            break;
          case 'c':       // combat pool
            sprintf(str, "%d", GET_COMBAT(d->character));
            break;
          case 'C':       // persona condition
            if (ch->persona)
              sprintf(str, "%d", ch->persona->condition);
            else
              sprintf(str, "NA");
            break;
          case 'd':       // defense pool
            sprintf(str, "%d", GET_DEFENSE(d->character));
            break;
          case 'e':
            if (ch->persona)
              sprintf(str, "%d", ch->persona->decker->active);
            else
              sprintf(str, "0");
            break;
          case 'E':
            if (ch->persona && ch->persona->decker->deck)
              sprintf(str, "%d", GET_OBJ_VAL(ch->persona->decker->deck, 2));
            else
              sprintf(str, "0");
            break;
          case 'g':       // current ammo
            if (GET_EQ(d->character, WEAR_WIELD) &&
                GET_OBJ_VAL(GET_EQ(d->character, WEAR_WIELD), 3) >= TYPE_GRENADE_LAUNCHER)
              if (GET_EQ(d->character, WEAR_WIELD)->contains) {
                sprintf(str, "%d", MIN(GET_OBJ_VAL(GET_EQ(d->character, WEAR_WIELD), 5),
                                       GET_OBJ_VAL(GET_EQ(d->character, WEAR_WIELD)->contains, 9)));
              } else
                sprintf(str, "0");
            else
              sprintf(str, "0");
            break;
          case 'G':       // max ammo
            if (GET_EQ(d->character, WEAR_WIELD) &&
                GET_OBJ_VAL(GET_EQ(d->character, WEAR_WIELD), 3) >= TYPE_GRENADE_LAUNCHER)
              sprintf(str, "%d", GET_OBJ_VAL(GET_EQ(d->character, WEAR_WIELD), 5));
            else
              sprintf(str, "0");
            break;
          case 'h':       // hacking pool
            sprintf(str, "%d", GET_HACKING(d->character));
            break;
          case 'i':       // impact
            sprintf(str, "%d", GET_IMPACT(d->character));
            break;
          case 'k':       // karma
            sprintf(str, "%0.2f", ((float)GET_KARMA(ch) / 100));
            break;
          case 'l':       // current weight
            sprintf(str, "%.2f", IS_CARRYING_W(d->character));
            break;
          case 'L':       // max weight
            sprintf(str, "%d", CAN_CARRY_W(d->character));
            break;
          case 'm':       // current mental
            sprintf(str, "%d", (int)(GET_MENTAL(d->character) / 100));
            break;
          case 'M':       // max mental
            sprintf(str, "%d", (int)(GET_MAX_MENTAL(d->character) / 100));
            break;
          case 'n':       // nuyen
            sprintf(str, "%d", GET_NUYEN(d->character));
            break;
          case 'o':       // offense pool
            sprintf(str, "%d", GET_OFFENSE(d->character));
            break;
          case 'p':       // current physical
            sprintf(str, "%d", (int)(GET_PHYSICAL(d->character) / 100));
            break;
          case 'P':       // max physical
            sprintf(str, "%d", (int)(GET_MAX_PHYSICAL(d->character) / 100));
            break;
          case 'r':
            if (ch->persona && ch->persona->decker->deck)
              sprintf(str, "%d", GET_OBJ_VAL(ch->persona->decker->deck, 3) - GET_OBJ_VAL(ch->persona->decker->deck, 5));
            else
              sprintf(str, "0");
            break;
          case 'R':
            if (ch->persona && ch->persona->decker && ch->persona->decker->deck)
              sprintf(str, "%d", GET_OBJ_VAL(ch->persona->decker->deck, 3));
            else
              sprintf(str, "0");
            break;
          case 's':       // current ammo
            if (GET_EQ(d->character, WEAR_HOLD) &&
                GET_OBJ_VAL(GET_EQ(d->character, WEAR_HOLD), 3) >= TYPE_GRENADE_LAUNCHER)
              if (GET_EQ(d->character, WEAR_HOLD)->contains) {
                sprintf(str, "%d", MIN(GET_OBJ_VAL(GET_EQ(d->character, WEAR_HOLD), 5),
                                       GET_OBJ_VAL(GET_EQ(d->character, WEAR_HOLD)->contains, 9)));
              } else
                sprintf(str, "0");
            else
              sprintf(str, "0");
            break;
          case 'S':       // max ammo
            if (GET_EQ(d->character, WEAR_HOLD) &&
                GET_OBJ_VAL(GET_EQ(d->character, WEAR_HOLD), 3) >= TYPE_GRENADE_LAUNCHER)
              sprintf(str, "%d", GET_OBJ_VAL(GET_EQ(d->character, WEAR_HOLD), 5));
            else
              sprintf(str, "0");
            break;
          case 't':       // magic pool
            sprintf(str, "%d", GET_MAGIC(d->character));
            break;
          case 'w':
            sprintf(str, "%d", GET_INVIS_LEV(d->character));
            break;
          case 'W':
            sprintf(str, "%d", GET_INCOG_LEV(d->character));
            break;
          case 'z':
            if (GET_REAL_LEVEL(d->character) >= LVL_BUILDER)
              sprintf(str, "%d", d->character->player_specials->saved.zonenum);
            else
              strcpy(str, "@z");
            break;
          case 'v':
            if (GET_REAL_LEVEL(d->character) >= LVL_BUILDER)
              sprintf(str, "%ld", world[d->character->in_room].number);
            else
              strcpy(str, "@v");
            break;
          case '@':
            strcpy(str, "@");
            break;
          default:
            sprintf(str, "@%c", *prompt);
            break;
          }
          for (j = 0; str[j]; j++, i++)
            temp[i] = str[j];
        } else {
          temp[i] = *prompt;
          i++;
        }
      }
      temp[i] = '\0';
      final = colorize(d, temp);
      write_to_descriptor(d->descriptor, final);
    }
  }
}

void write_to_q(char *txt, struct txt_q * queue, int aliased)
{
  struct txt_block *temp = new txt_block;
  temp->text = new char[strlen(txt) + 1];
  strcpy(temp->text, txt);
  temp->aliased = aliased;

  /* queue empty? */
  if (!queue->head)
  {
    temp->next = NULL;
    queue->head = queue->tail = temp;
  } else
  {
    queue->tail->next = temp;
    queue->tail = temp;
    temp->next = NULL;
  }
}

int get_from_q(struct txt_q * queue, char *dest, int *aliased)
{
  struct txt_block *tmp;

  /* queue empty? */
  if (!queue->head)
    return 0;

  tmp = queue->head;
  strcpy(dest, queue->head->text);
  *aliased = queue->head->aliased;
  queue->head = queue->head->next;

  delete [] tmp->text;
  delete tmp;

  return 1;
}

/* Empty the queues before closing connection */
void flush_queues(struct descriptor_data * d)
{
  int dummy;

  if (d->large_outbuf)
  {
    d->large_outbuf->next = bufpool;
    bufpool = d->large_outbuf;
  }
  while (get_from_q(&d->input, buf2, &dummy))
    ;
}

/* Add a new string to a player's output queue */
void write_to_output(const char *txt, struct descriptor_data *t)
{
  int size;

  txt = colorize(t, (char *)txt);

  size = strlen(txt);

  /* if we're in the overflow state already, ignore this new output */
  if (t->bufptr < 0)
    return;

  /* if we have enough space, just write to buffer and that's it! */
  if (t->bufspace >= size)
  {
    strcpy(t->output + t->bufptr, txt);
    t->bufspace -= size;
    t->bufptr += size;
    return;
  }

  /*
   * If we're already using the large buffer, or if even the large buffer
   * is too small to handle this new text, chuck the text and switch to the
   * overflow state.
   */
  if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE))
  {
    t->bufptr = -1;
    buf_overflows++;
    return;
  }

  buf_switches++;

  /* if the pool has a buffer in it, grab it */
  if (bufpool != NULL)
  {
    t->large_outbuf = bufpool;
    bufpool = bufpool->next;
  } else
  {                      /* else create a new one */
    t->large_outbuf = new txt_block;
    t->large_outbuf->text = new char[LARGE_BUFSIZE];
    buf_largecount++;
  }

  strcpy(t->large_outbuf->text, t->output);     /* copy to big buffer */
  t->output = t->large_outbuf->text;    /* make big buffer primary */
  strcat(t->output, txt);       /* now add new text */

  /* calculate how much space is left in the buffer */
  t->bufspace = LARGE_BUFSIZE - 1 - strlen(t->output);

  /* set the pointer for the next write */
  t->bufptr = strlen(t->output);
}

/* ******************************************************************
*  socket handling                                                  *
****************************************************************** */
void init_descriptor (struct descriptor_data *newd, int desc)
{
  static int last_desc = 0;  /* last descriptor number */

  newd->descriptor = desc;
  newd->connected = CON_GET_NAME;
  newd->idle_tics = 0;
  newd->wait = 1;
  newd->output = newd->small_outbuf;
  newd->bufspace = SMALL_BUFSIZE - 1;
  newd->next = descriptor_list;
  newd->login_time = time (0);

  if (++last_desc == 1000)
    last_desc = 1;
  newd->desc_num = last_desc;
}

int new_descriptor(int s)
{
  int desc, sockets_connected = 0;
  unsigned long addr;
  int i;
  struct descriptor_data *newd;
  struct sockaddr_in peer;
  struct hostent *from;
  extern char *GREETINGS;

  /* accept the new connection */
  i = sizeof(peer);
#if defined(__CYGWIN__) || defined(WIN32)

  if ((desc = accept(s, (struct sockaddr *) &peer, &i)) < 0) {
    perror("accept");
    return -1;
#else

  if ((desc = accept(s, (struct sockaddr *) &peer, (unsigned *) &i)) < 0)
  {
    perror("accept");
    return -1;
#endif

  }
  /* keep it from blocking */
  nonblock(desc);

  /* make sure we have room for it */
  for (newd = descriptor_list; newd; newd = newd->next)
    sockets_connected++;

  if (sockets_connected >= max_players)
  {
    write_to_descriptor(desc, "Sorry, Awake II: Seattle 2062 is full right now... try again later!  :-)\r\n");
    close(desc);
    return 0;
  }
  /* create a new descriptor */
  newd = new descriptor_data;
  memset((char *) newd, 0, sizeof(struct descriptor_data));

  /* find the sitename */
  if (nameserver_is_slow || !(from = gethostbyaddr((char *) &peer.sin_addr,
                                     sizeof(peer.sin_addr), AF_INET)))
  {
    if (!nameserver_is_slow)
      perror("gethostbyaddr");
    addr = ntohl(peer.sin_addr.s_addr);
    sprintf(newd->host, "%03u.%03u.%03u.%03u", (int) ((addr & 0xFF000000) >> 24),
            (int) ((addr & 0x00FF0000) >> 16), (int) ((addr & 0x0000FF00) >> 8),
            (int) ((addr & 0x000000FF)));
  } else
  {
    strncpy(newd->host, from->h_name, HOST_LENGTH);
    *(newd->host + HOST_LENGTH) = '\0';
  }

  /* determine if the site is banned */
  if (isbanned(newd->host) == BAN_ALL)
  {
    close(desc);
    sprintf(buf2, "Connection attempt denied from [%s]", newd->host);
    mudlog(buf2, NULL, LOG_BANLOG, TRUE);
    delete newd;
    return 0;
  }

  init_descriptor(newd, desc);

  /* prepend to list */
  descriptor_list = newd;

  SEND_TO_Q(GREETINGS, newd);
  /*
  ident_start(newd, peer.sin_addr.s_addr);
  */

  return 0;
}

int process_output(struct descriptor_data *t) {
  static char i[LARGE_BUFSIZE + GARBAGE_SPACE];
  static int result;

  /* If the descriptor is NULL, just return */
  if ( !t )
    return 0;

  /* we may need this \r\n for later -- see below */
  strcpy(i, "\r\n");

  /* now, append the 'real' output */
  strcpy(i + 2, t->output);

  /* if we're in the overflow state, notify the user */
  if (t->bufptr < 0)
    strcat(i, "**OVERFLOW**");

  /* add the extra CRLF if the person isn't in compact mode */
  if (!t->connected && t->character)
    strcat(i + 2, "\r\n");

  /*
   * now, send the output.  If this is an 'interruption', use the prepended
   * CRLF, otherwise send the straight output sans CRLF.
   */
  if (!t->prompt_mode)          /* && !t->connected) */
    result = write_to_descriptor(t->descriptor, i);
  else
    result = write_to_descriptor(t->descriptor, i + 2);

  /* handle snooping: prepend "% " and send to snooper */
  if (t->snoop_by)
  {
    SEND_TO_Q("% ", t->snoop_by);
    SEND_TO_Q(t->output, t->snoop_by);
    SEND_TO_Q("%%", t->snoop_by);
  }
  /*
   * if we were using a large buffer, put the large buffer on the buffer pool
   * and switch back to the small one
   */
  if (t->large_outbuf)
  {
    t->large_outbuf->next = bufpool;
    bufpool = t->large_outbuf;
    t->large_outbuf = NULL;
    t->output = t->small_outbuf;
  }
  /* reset total bufspace back to that of a small buffer */
  t->bufspace = SMALL_BUFSIZE - 1;
  t->bufptr = 0;
  *(t->output) = '\0';

  return result;
}

int write_to_descriptor(int desc, char *txt) {
  int total, bytes_written;

  total = strlen(txt);

  do {
    if ((bytes_written = write(desc, txt, total)) < 0) {
#ifdef EWOULDBLOCK
      if (errno == EWOULDBLOCK)
        errno = EAGAIN;
#endif

      if (errno == EAGAIN)
        log("process_output: socket write would block, about to close");
      else
        perror("Write to socket");
      return -1;
    } else {
      txt += bytes_written;
      total -= bytes_written;
    }
  } while (total > 0);

  return 0;
}

/*
 * ASSUMPTION: There will be no newlines in the raw input buffer when this
 * function is called.  We must maintain that before returning.
 */
int process_input(struct descriptor_data *t) {
  int buf_length, bytes_read, space_left, failed_subst;
  char *ptr, *read_point, *write_point, *nl_pos = NULL;
  char tmp[MAX_INPUT_LENGTH + 8];

  /* first, find the point where we left off reading data */
  buf_length = strlen(t->inbuf);
  read_point = t->inbuf + buf_length;
  space_left = MAX_RAW_INPUT_LENGTH - buf_length - 1;

  do
  {
    if (space_left <= 0) {
      log("process_input: about to close connection: input overflow");
      return -1;
    }

    if ((bytes_read = read(t->descriptor, read_point, space_left)) < 0) {

#ifdef EWOULDBLOCK
      if (errno == EWOULDBLOCK)
        errno = EAGAIN;
#endif

      if (errno != EAGAIN) {
        perror("process_input: about to lose connection");
        return -1;              /* some error condition was encountered on
                                                                                                                         * read */
      } else
        return 0;               /* the read would have blocked: just means no
                                                                                                   * data there */
    } else if (bytes_read == 0) {
      // log("EOF on socket read (connection broken by peer)");
      return -1;
    }
    /* at this point, we know we got some data from the read */

    *(read_point + bytes_read) = '\0';  /* terminate the string */

    /* search for a newline in the data we just read */
    for (ptr = read_point; *ptr && !nl_pos; ptr++)
      if (ISNEWL(*ptr))
        nl_pos = ptr;

    read_point += bytes_read;
    space_left -= bytes_read;

    /*
     * on some systems such as AIX, POSIX-standard nonblocking I/O is broken,
     * causing the MUD to hang when it encounters input not terminated by a
     * newline.  This was causing hangs at the Password: prompt, for example.
     * I attempt to compensate by always returning after the _first_ read, instead
     * of looping forever until a read returns -1.  This simulates non-blocking
     * I/O because the result is we never call read unless we know from select()
     * that data is ready (process_input is only called if select indicates that
     * this descriptor is in the read set).  JE 2/23/95.
     */
#if !defined(POSIX_NONBLOCK_BROKEN)

  } while (nl_pos == NULL);
#else

  }
  while (0)
    ;

  if (nl_pos == NULL)
    return 0;
#endif

  /*
   * okay, at this point we have at least one newline in the string; now we
   * can copy the formatted data to a new array for further processing.
   */

  read_point = t->inbuf;

  while (nl_pos != NULL) {
    write_point = tmp;
    space_left = MAX_INPUT_LENGTH - 1;

    for (ptr = read_point; (space_left > 0) && (ptr < nl_pos); ptr++) {
      if (*ptr == '\b') {       /* handle backspacing */
        if (write_point > tmp) {
          if (*(--write_point) == '$') {
            write_point--;
            space_left += 2;
          } else
            space_left++;
        }
      } else if (isprint(*ptr)) {
        if ((*(write_point++) = *ptr) == '$') { /* copy one character */
          *(write_point++) = '$';
          space_left -= 2;
        } else
          space_left--;
      }
    }
    *write_point = '\0';

    if ((space_left <= 0) && (ptr < nl_pos)) {
      char buffer[MAX_INPUT_LENGTH + 64];

      sprintf(buffer, "Line too long.  Truncated to:\r\n%s\r\n", tmp);
      if (write_to_descriptor(t->descriptor, buffer) < 0)
        return -1;
    }
    if (t->snoop_by) {
      SEND_TO_Q("% ", t->snoop_by);
      SEND_TO_Q(tmp, t->snoop_by);
      SEND_TO_Q("\r\n", t->snoop_by);
    }
    failed_subst = 0;

    if (*tmp == '!' && t->connected != CON_CNFPASSWD )
      strcpy(tmp, t->last_input);
    else if (*tmp == '|' && t->connected != CON_CNFPASSWD ) {
      if (!(failed_subst = perform_subst(t, t->last_input, tmp)))
        strcpy(t->last_input, tmp);
    } else
      strcpy(t->last_input, tmp);

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

    /* find the end of this line */
    while (ISNEWL(*nl_pos))
      nl_pos++;

    /* see if there's another newline in the input buffer */
    read_point = ptr = nl_pos;
    for (nl_pos = NULL; *ptr && !nl_pos; ptr++)
      if (ISNEWL(*ptr))
        nl_pos = ptr;
  }

  /* now move the rest of the buffer up to the beginning for the next pass */
  write_point = t->inbuf;
  while (*read_point)
    *(write_point++) = *(read_point++);
  *write_point = '\0';

  return 1;
}

/*
 * perform substitution for the '^..^' csh-esque syntax
 * orig is the orig string (i.e. the one being modified.
 * subst contains the substition string, i.e. "^telm^tell"
 */
int perform_subst(struct descriptor_data *t, char *orig, char *subst)
{
  char New[MAX_INPUT_LENGTH + 5];

  char *first, *second, *strpos;

  /*
   * first is the position of the beginning of the first string (the one
   * to be replaced
   */
  first = subst + 1;

  /* now find the second '^' */
  if (!(second = strchr((const char *)first, '^')))
  {
    SEND_TO_Q("Invalid substitution.\r\n", t);
    return 1;
  }

  /* terminate "first" at the position of the '^' and make 'second' point
   * to the beginning of the second string */
  *(second++) = '\0';

  /* now, see if the contents of the first string appear in the original */
  if (!(strpos = strstr((const char *)orig, first)))
  {
    SEND_TO_Q("Invalid substitution.\r\n", t);
    return 1;
  }

  /* now, we construct the new string for output. */

  /* first, everything in the original, up to the string to be replaced */
  strncpy(New, orig, (strpos - orig));
  New[(strpos - orig)] = '\0';

  /* now, the replacement string */
  strncat(New, second, (MAX_INPUT_LENGTH - strlen(New) - 1));

  /* now, if there's anything left in the original after the string to
   * replaced, copy that too. */
  if (((strpos - orig) + strlen(first)) < strlen(orig))
    strncat(New, strpos + strlen(first), (MAX_INPUT_LENGTH - strlen(New) - 1));

  /* terminate the string in case of an overflow from strncat */
  New[MAX_INPUT_LENGTH-1] = '\0';
  strcpy(subst, New);

  return 0;
}

void free_editing_structs(descriptor_data *d)
{
  if (d->edit_obj) {
    Mem->DeleteObject(d->edit_obj);
    d->edit_obj = NULL;
  }

  if (d->edit_room) {
    Mem->DeleteRoom(d->edit_room);
    d->edit_room = NULL;
  }

  if (d->edit_mob) {
    Mem->DeleteCh(d->edit_mob);
    d->edit_mob = NULL;
  }

  if (d->edit_quest) {
    free_quest(d->edit_quest);
    delete d->edit_quest;
    d->edit_quest = NULL;
  }

  if (d->edit_shop) {
    free_shop(d->edit_shop);
    delete d->edit_shop;
    d->edit_shop = NULL;
  }

  if (d->edit_zon) {
    if (d->edit_zon->name)
      delete [] d->edit_zon->name;
    delete d->edit_zon;
    d->edit_zon = NULL;
  }

  if (d->edit_cmd) {
    delete d->edit_cmd;
    d->edit_cmd = NULL;
  }

  if (d->edit_spell) {
    if (d->edit_spell->name)
      delete [] d->edit_spell->name;
    delete d->edit_spell;
    d->edit_spell = NULL;
  }
}

void close_socket(struct descriptor_data *d)
{
  struct descriptor_data *temp;
  spell_t *one, *next;
  char buf[128];

  close(d->descriptor);
  flush_queues(d);

  /*
  if (d->ident_sock != -1)
    close(d->ident_sock);
    */

  /* Forget snooping */
  if (d->snooping)
    d->snooping->snoop_by = NULL;

  if (d->snoop_by)
  {
    SEND_TO_Q("Your victim is no longer among us.\r\n", d->snoop_by);
    d->snoop_by->snooping = NULL;
  }
  if (d->character)
  {
    /* added to Free up temporary editing constructs */
    if (d->connected == CON_PLAYING || (d->connected >= CON_SPELL_CREATE &&
                                        d->connected <= CON_ZEDIT)) {
      playerDB.SaveChar(d->character);
      act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
      sprintf(buf, "Closing link to: %s.", GET_CHAR_NAME(d->character));
      mudlog(buf, d->character, LOG_CONNLOG, TRUE);

      free_editing_structs(d);

      d->character->desc = NULL;
    } else {
      sprintf(buf, "Losing player: %s.",
              GET_CHAR_NAME(d->character) ? GET_CHAR_NAME(d->character) : "<null>");
      mudlog(buf, d->character, LOG_CONNLOG, TRUE);
      // we do this because objects can be given to characters in chargen
      for (int i = 0; i < NUM_WEARS; i++)
        if (GET_EQ(d->character, i))
          extract_obj(GET_EQ(d->character, i));
      while (d->character->carrying)
        extract_obj(d->character->carrying);
      if (d->character->spells)
        for (one = d->character->spells; one; one = next) {
          next = one->next;
          if (one->name)
            delete [] one->name;
          delete one;
        }
      Mem->DeleteCh(d->character);
    }
  } // else mudlog("Losing descriptor without char.", LVL_BUILDER, LOG_CONNLOG, FALSE);

  /* JE 2/22/95 -- part of my enending quest to make switch stable */
  if (d->original && d->original->desc)
    d->original->desc = NULL;

  REMOVE_FROM_LIST(d, descriptor_list, next);

  delete d;
}

void check_idle_passwords(void)
{
  struct descriptor_data *d;

  for (d = descriptor_list; d; d = d->next) {
    if (STATE(d) != CON_PASSWORD && STATE(d) != CON_GET_NAME)
      continue;

    if (++d->idle_tics >= 12 ) /* 120 seconds RL, or 2 minutes */
      if (STATE(d) == CON_PASSWORD)
        echo_on(d);
    SEND_TO_Q("\r\nTimed out... goodbye.\r\n", d);
    STATE(d) = CON_CLOSE;
  }
}


/*
 * I tried to universally convert Circle over to POSIX compliance, but
 * alas, some systems are still straggling behind and don't have all the
 * appropriate defines.  In particular, NeXT 2.x defines O_NDELAY but not
 * O_NONBLOCK.  Krusty old NeXT machines!  (Thanks to Michael Jones for
 * this and various other NeXT fixes.)
 */
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif

#if defined(WIN32) && !defined(__CYGWIN__)
void nonblock(socket_t s)
{
  unsigned long val = 1;
  ioctlsocket(s, FIONBIO, &val);
}
#else

void nonblock(int s)
{
  int flags;

  flags = fcntl(s, F_GETFL, 0);
  flags |= O_NONBLOCK;
  if (fcntl(s, F_SETFL, flags) < 0) {
    perror("Fatal error executing nonblock (comm.c)");
    exit(1);
  }
}
#endif

void shutdown(int code)
{
  circle_shutdown = true;
  exit_code = code;
}

/* ******************************************************************
*  signal-handling functions (formerly signals.c)                   *
****************************************************************** */


void checkpointing(int Empty)
{
  if (!tics) {
    log("SYSERR: CHECKPOINT shutdown: tics not updated");
    abort();
  } else
    tics = 0;
}



void unrestrict_game(int Empty)
{
  extern struct ban_list_element *ban_list;

  mudlog("Received SIGUSR2 - completely unrestricting game (emergency)",
         NULL, LOG_SYSLOG, TRUE);
  ban_list = NULL;
  restrict = 0;
}

void free_up_memory(int Empty)
{
  mudlog("Warning: Received signal, Freeing up Memory", NULL, LOG_SYSLOG, TRUE);
  if (!Mem->ClearStacks()) {
    cerr << "SYSERR: Unable to free enough memory, shutting down...\n";
    House_save_all();
    exit(0);
  }
}

void hupsig(int Empty)
{
  mudlog("Received SIGHUP.  Shutting down...", NULL, LOG_SYSLOG, TRUE);
  House_save_all();
  exit(0);
}

void intsig(int Empty)
{
  mudlog("Received SIGINT.  Shutting down...", NULL, LOG_SYSLOG, TRUE);
  House_save_all();
  exit(0);
}

void termsig(int Empty)
{
  mudlog("Received SIGTERM.  Shutting down...", NULL, LOG_SYSLOG, TRUE);
  House_save_all();
  exit(0);
}



/*
 * This is an implementation of signal() using sigaction() for portability.
 * (sigaction() is POSIX; signal() is not.)  Taken from Stevens' _Advanced
 * Programming in the UNIX Environment_.  We are specifying that all system
 * calls _not_ be automatically restarted for uniformity, because BSD systems
 * do not restart select(), even if SA_RESTART is used.
 *
 * Note that NeXT 2.x is not POSIX and does not have sigaction; therefore,
 * I just define it to be the old signal.  If your system doesn't have
 * sigaction either, you can use the same fix.
 *
 * SunOS Release 4.0.2 (sun386) needs this too, according to Tim Aldric.
 */


#if defined(NeXT) || defined(sun386) || (defined(WIN32) && !defined(__CYGWIN__))
#define my_signal(signo, func) signal(signo, func)
#else
sigfunc *my_signal(int signo, sigfunc * func)
{
  struct sigaction act, oact;

  act.sa_handler = func;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
#ifdef SA_INTERRUPT

  act.sa_flags |= SA_INTERRUPT; /* SunOS */
#endif

  if (sigaction(signo, &act, &oact) < 0)
    return SIG_ERR;

  return oact.sa_handler;
}
#endif /* NeXT */

void signal_setup(void)
{
  my_signal(SIGINT, hupsig);
  my_signal(SIGTERM, hupsig);
#if !defined(WIN32) || defined(__CYGWIN__)

  my_signal(SIGPIPE, SIG_IGN);
  my_signal(SIGALRM, SIG_IGN);
#endif
}



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

int is_color (char c)
{
  int i = 0;

  switch (LOWER(c)) {
  case 'b':
  case 'c':
  case 'g':
  case 'l':
  case 'm':
  case 'r':
  case 'w':
  case 'y':
    i = 2;
    break;
  default:
    break;
  }
  if (i && c < 'a')
    i--;

  return i;
}

void html_colorize(FILE *html, char *str)
{
  char *gnu, mode = 0;

  if (!strchr(str, '^')) {
    fprintf(html, "%s<br>\n", str);
    return;
  }

  gnu = new char[MAX_RAW_INPUT_LENGTH];

  *gnu = '\0';

  for (; *str; str++) {
    if (*str == '^') {
      str++;
      if (LOWER(*str) == 'n') {
        if ((mode % 2) == 1)
          strcat(gnu, "</font>");
        if (mode > 1)
          strcat(gnu, "</b>");
        mode = 0;
      } else if (is_color(*str) == 1) {
        if ((mode % 2) == 1)
          strcat(gnu, "</font>");
        if (mode < 2)
          strcat(gnu, "<b>");
        mode = 3;
      } else if (is_color(*str) == 2) {
        if ((mode % 2) == 1)
          strcat(gnu, "</font>");
        if (mode > 1)
          strcat(gnu, "</b>");
        mode = 1;
      }
      switch (LOWER(*str)) {
      case 'b':
        strcat(gnu, "<font color=\"#0000FF\">");
        break;
      case 'c':
        strcat(gnu, "<font color=\"#00FFFF\">");
        break;
      case 'g':
        strcat(gnu, "<font color=\"#00FF00\">");
        break;
      case 'l':
        strcat(gnu, "<font color=\"#7F7F7F\">");
        break;
      case 'm':
        strcat(gnu, "<font color=\"#FF00FF\">");
        break;
      case 'n':
        break;
      case 'r':
        strcat(gnu, "<font color=\"#FF0000\">");
        break;
      case 'w':
        strcat(gnu, "<font color=\"#FFFFFF\">");
        break;
      case 'y':
        strcat(gnu, "<font color=\"#FFFF00\">");
        break;
      case '^':
        strcat(gnu, "^");
        break;
      default:
        sprintf(gnu, "%s^%c", gnu, *str);
        break;
      }
    } else
      sprintf(gnu, "%s%c", gnu, *str);
  }
  fprintf(html, "%s<br>\n", gnu);
}

// parses the color codes and translates the properly--we assume that
// str is not NULL
char *colorize(struct descriptor_data *d, char *str)
{
  // it is possible that this could over flow if only because the color
  // codes take up several characters--however, I certainly am not
  // gonna allocate a new one every time (=
  static char buffer[MAX_STRING_LENGTH];
  register char *temp = &buffer[0];
  register const char *color;

  if (d->character && (COLOR_LEV(d->character) > C_SPR))
  {
    while(*str) {
      if (*str == '^') {
        // first, advance the pointer, then point color to the color
        switch (*++str) {
        case 'l':
          color = KBLK;
          break; // black
        case 'r':
          color = KRED;
          break; // red
        case 'g':
          color = KGRN;
          break; // green
        case 'y':
          color = KYEL;
          break; // yellow
        case 'b':
          color = KBLU;
          break; // blue
        case 'm':
          color = KMAG;
          break; // magenta
        case 'n':
          color = KNRM;
          break; // normal
        case 'c':
          color = KCYN;
          break; // cyan
        case 'w':
          color = KWHT;
          break; // white
        case 'L':
          color = B_BLK;
          break; // bold black
        case 'R':
          color = B_RED;
          break; // bold red
        case 'G':
          color = B_GREEN;
          break; // bold green
        case 'Y':
          color = B_YELLOW;
          break; // bold yellow
        case 'B':
          color = B_BLUE;
          break; // bold blue
        case 'M':
          color = B_MAGENTA;
          break; // bold magenta
        case 'N':
          color = CGLOB;
          break;
        case 'C':
          color = B_CYAN;
          break; // bold cyan
        case 'W':
          color = B_WHITE;
          break; // bold white
        case '^':
          color = "^";
          break;
          //     case 'x': color = newline; break;
          //     case 't': color = tab; break;
        default:
          color = NULL;
          break;
        }
        // somehow I get the feeling there is a better way to do this
        if (color) {
          while (*color)
            *temp++ = *color++;
          ++str;
        } else {
          *temp++ = '^';
          *temp++ = *str++;
        }
      } else
        *temp++ = *str++;
      // in the case where it is nothing, we just copy and advance
    }
    // now cap it off with the off-color set of ansi-escape sequences
    // as a preventive measure
    color = KNRM;
    while (*color)
      *temp++ = *color++;
  } else
  {
    while(*str) {
      if (*str == '^') {
        // first, advance the pointer, then point color to the color
        switch (*++str) {
        case 'l':
        case 'r':
        case 'g':
        case 'y':
        case 'b':
        case 'm':
        case 'n':
        case 'c':
        case 'w':
        case 'L':
        case 'R':
        case 'G':
        case 'Y':
        case 'B':
        case 'N':
        case 'M':
        case 'C':
        case 'W':
          color = "";
          break;
          //      case 'x':
          //        color = newline;
          //        break;
          //      case 't':
          //        color = tab;
          //        break;
        case '^':
          color = "^";
          break;
        default:
          color = NULL;
          break;
        }
        // first we check to see if it was nothing, ie none of the sequences
        if (!color) {
          // and if it is not, we copy ^ first, then the value of *str
          // and advance again
          *temp++ = '^';
          *temp++ = *str++;
        } else if (*color == '\0')
          // we check to see if this is a case of a color sequence
          str++;
        // and if it is, we advance the pointer to eat it up
        // and the last case is if it is a ^, in which case we just copy one
        // ^ into it, as we do here
        else if(*color != '\0') {
          str++;
          while (*color)
            *temp++ = *color++;
        } else
          *temp++ = *str++;
      } else {
        *temp++ = *str++;
      }
    }
  }
  // and terminate it with NULL properly
  *temp = '\0';

  return &buffer[0];
}

char buf3[MAX_STRING_LENGTH];
void send_to_char(struct char_data * ch, const char * const messg, ...)
{
  if (!ch->desc || !messg)
    return;

  va_list argptr;
  va_start(argptr, messg);
  vsprintf(buf3, messg, argptr);
  va_end(argptr);
  SEND_TO_Q(buf3, ch->desc);
}

void send_to_obj(struct obj_data *obj, const char * const messg, ...)
{
  if (!obj || !messg)
    return;

  // this formats the argument
  va_list argptr;
  va_start(argptr, messg);
  vsprintf(buf3, messg, argptr);
  va_end(argptr);

  // then we send messages to the appropriate folks
  // here, it is in the inventory, so only that person sees the message
  if (obj->carried_by && obj->carried_by->desc)
  {
    SEND_TO_Q(buf3, obj->carried_by->desc);
    return;
  }

  // as before, only the person wearing this sees the message
  if (obj->worn_by && obj->worn_by->desc)
  {
    SEND_TO_Q(buf3, obj->worn_by->desc);
    return;
  }

}

void send_to_char(char *messg, struct char_data * ch)
{
  if (ch->desc && messg)
    SEND_TO_Q(messg, ch->desc);
}

void send_to_char(const char *messg, struct char_data *ch)
{
  if (ch->desc && messg)
    SEND_TO_Q(messg, ch->desc);
}

void send_to_icon(struct matrix_icon * icon, const char * const messg, ...)
{
  if (!icon || !icon->decker || !messg)
    return;

  va_list argptr;
  va_start(argptr, messg);
  vsprintf(buf3, messg, argptr);
  va_end(argptr);
  if (icon->decker->ch && icon->decker->ch->desc)
    SEND_TO_Q(buf3, icon->decker->ch->desc);
  if (icon->decker->hitcher && icon->decker->hitcher->desc)
    SEND_TO_Q(buf3, icon->decker->hitcher->desc);
}


void send_to_all(char *messg)
{
  struct descriptor_data *i;

  if (messg)
    for (i = descriptor_list;i; i = i->next)
      if (!i->connected) {
        SEND_TO_Q(messg, i);
      }
}

void send_to_outdoor(char *messg)
{
  struct descriptor_data *i;

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

  for (i = descriptor_list; i; i = i->next)
    if (!i->connected && i->character && AWAKE(i->character) &&
        !(PLR_FLAGGED(i->character, PLR_WRITING) ||
          PLR_FLAGGED(i->character, PLR_EDITING) ||
          PLR_FLAGGED(i->character, PLR_MAILING)) &&
        OUTSIDE(i->character)) {
      SEND_TO_Q(messg, i);
    }
}

void send_to_driver(char *messg, struct veh_data *veh)
{
  struct char_data *i;

  if (messg)
    for (i = veh->people; i; i = i->next_in_veh)
      if (AFF_FLAGGED(i, AFF_PILOT) && i->desc)
        SEND_TO_Q(messg, i->desc);
}

void send_to_host(vnum_t room, char *messg, struct matrix_icon *icon, bool needsee)
{
  struct matrix_icon *i;
  if (messg)
    for (i = matrix[room].icons; i; i = i->next_in_host)
      if (icon != i && i->decker)
        if (!needsee || (needsee && has_spotted(i, icon)))
          send_to_icon(i, messg);
}
void send_to_veh(char *messg, struct veh_data *veh, struct char_data *ch, bool torig, ...)
{
  struct char_data *i;

  if (messg)
  {
    for (i = veh->people; i; i = i->next_in_veh)
      if (i != ch && i->desc) {
        if (!(!torig && AFF_FLAGGED(i, AFF_RIG)))
          SEND_TO_Q(messg, i->desc);
      }
    if (torig && veh->rigger)
      SEND_TO_Q(messg, veh->rigger->desc);
  }
}

void send_to_veh(char *messg, struct veh_data *veh, struct char_data *ch, struct char_data *cha, bool torig)
{
  struct char_data *i;

  if (messg)
  {
    for (i = veh->people; i; i = i->next_in_veh)
      if (i != ch && i != cha && i->desc) {
        if (!(!torig && AFF_FLAGGED(i, AFF_RIG)))
          SEND_TO_Q(messg, i->desc);
      }
    if (torig && veh->rigger)
      SEND_TO_Q(messg, veh->rigger->desc);
  }
}

void send_to_room(char *messg, int room)
{
  struct char_data *i;

  if (messg)
    for (i = world[room].people; i; i = i->next_in_room)
      if (i->desc)
        if (!(PLR_FLAGGED(i, PLR_REMOTE) || PLR_FLAGGED(i, PLR_MATRIX)) && AWAKE(i))
          SEND_TO_Q(messg, i->desc);

}

char *ACTNULL = "<NULL>";

#define CHECK_NULL(pointer, expression)  if ((pointer) == NULL) i = ACTNULL; else i = (expression);


/* higher-level communication: the act() function */
void perform_act(char *orig, struct char_data * ch, struct obj_data * obj,
                 void *vict_obj, struct char_data * to)
{
  extern char *make_desc(char_data *ch, char_data *i, char *buf, int act);
  register const char *i = NULL;
  register char *buf;
  struct char_data *vict;
  static char lbuf[MAX_STRING_LENGTH];
  struct remem *mem = NULL;
  char temp[MAX_STRING_LENGTH];
  buf = lbuf;
  vict = (struct char_data *) vict_obj;

  for (;;)
  {
    if (*orig == '$') {
      switch (*(++orig)) {
      case 'n':
        if (to == ch)
          i = "you";
        else if (CAN_SEE(to, ch))
          if (IS_SENATOR(to) && !IS_NPC(ch))
            i = GET_CHAR_NAME(ch);
          else
            i = make_desc(to, ch, buf, TRUE);
        else
          i = "someone";
        break;
      case 'N':
        if (to == vict)
          i = "you";
        else if (CAN_SEE(to, vict))
          if (IS_SENATOR(to) && !IS_NPC(vict))
            i = GET_CHAR_NAME(vict);
          else if ((mem = found_mem(GET_MEMORY(to), vict)))
            i = CAP(mem->mem);
          else
            i = GET_NAME(vict);
        else
          i = "someone";
        break;
        break;
      case 'v':
        if (IS_NPC(ch))
          i = GET_NAME(ch);
        else
          if (IS_SENATOR(to)) {
            sprintf(temp, "%s(%s)", ch->player.physical_text.room_desc, GET_CHAR_NAME(ch));
            i = temp;
          } else if ((mem = found_mem(GET_MEMORY(to), ch))) {
            sprintf(temp, "%s(%s)", ch->player.physical_text.room_desc, CAP(mem->mem));
            i = temp;
          } else
            i = ch->player.physical_text.room_desc;
        break;
      case 'm':
        i = HMHR(ch);
        break;
      case 'M':
        CHECK_NULL(vict_obj, HMHR(vict));
        break;
      case 's':
        i = HSHR(ch);
        break;
      case 'S':
        CHECK_NULL(vict_obj, HSHR(vict));
        break;
      case 'e':
        i = HSSH(ch);
        break;
      case 'E':
        CHECK_NULL(vict_obj, HSSH(vict));
        break;
      case 'o':
        CHECK_NULL(obj, OBJN(obj, to));
        break;
      case 'O':
        CHECK_NULL(vict_obj, OBJN((struct obj_data *) vict_obj, to));
        break;
      case 'p':
        CHECK_NULL(obj, OBJS(obj, to));
        break;
      case 'P':
        CHECK_NULL(vict_obj, OBJS((struct obj_data *) vict_obj, to));
        break;
      case 'a':
        CHECK_NULL(obj, SANA(obj));
        break;
      case 'A':
        CHECK_NULL(vict_obj, SANA((struct obj_data *) vict_obj));
        break;
      case 'T':
        CHECK_NULL(vict_obj, (char *) vict_obj);
        break;
      case 'F':
        CHECK_NULL(vict_obj, fname((char *) vict_obj));
        break;
      case '$':
      default:
        //        i = ((char *)orig--);
        i = "$";
        break;
      }
      while ((*buf = *(i++)))
        buf++;
      orig++;
    } else if (!(*(buf++) = *(orig++)))
      break;
  }

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

  SEND_TO_Q(CAP(lbuf), to->desc);
}

/* modified to include spell creation menu */
#define SENDOK(ch) ((ch)->desc && (AWAKE(ch) || sleep) && !(PLR_FLAGGED((ch), PLR_WRITING) || PLR_FLAGGED((ch), PLR_EDITING) || PLR_FLAGGED((ch), PLR_MAILING) || PLR_FLAGGED((ch), PLR_CUSTOMIZE)) && (STATE(ch->desc) != CON_SPELL_CREATE))

void act(char *str, int hide_invisible, struct char_data * ch,
         struct obj_data * obj, void *vict_obj, int type)
{
  struct char_data *to;
  int sleep;

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

  /*
   * Warning: the following TO_SLEEP code is a hack.
   *
   * I wanted to be able to tell act to deliver a message regardless of sleep
   * without adding an additional argument.  TO_SLEEP is 128 (a single bit
   * high up).  It's ONLY legal to combine TO_SLEEP with one other TO_x
   * command.  It's not legal to combine TO_x's with each other otherwise.
   */

  /* check if TO_SLEEP is there, and remove it if it is. */
  if ((sleep = (type & TO_SLEEP)))
    type &= ~TO_SLEEP;

  if ( type == TO_ROLLS )
    sleep = 1;

  if (type == TO_CHAR)
  {
    if (ch && SENDOK(ch) && !(PLR_FLAGGED(ch, PLR_REMOTE) || PLR_FLAGGED(ch, PLR_MATRIX)))
      perform_act(str, ch, obj, vict_obj, ch);
    return;
  }
  if (type == TO_VICT)
  {
    if ((to = (struct char_data *) vict_obj) && SENDOK(to) &&
        !(PLR_FLAGGED(to, PLR_REMOTE) || PLR_FLAGGED(to, PLR_MATRIX) && !sleep) &&
        !(hide_invisible && ch && !CAN_SEE(to, ch)))
      perform_act(str, ch, obj, vict_obj, to);
    return;
  }
  if (type == TO_DECK)
  {
    if ((to = (struct char_data *) vict_obj) && SENDOK(to) && PLR_FLAGGED(to, PLR_MATRIX) &&
        !(hide_invisible && ch && !CAN_SEE(to, ch)))
      perform_act(str, ch, obj, vict_obj, to);
    return;
  }
  /* ASSUMPTION: at this point we know type must be TO_NOTVICT
       or TO_ROOM or TO_ROLLS */

  if (ch && ch->in_room != NOWHERE)
    to = world[ch->in_room].people;
  else if (obj && obj->in_room != NOWHERE)
    to = world[obj->in_room].people;
  else if (ch && ch->in_veh)
    to = ch->in_veh->people;
  else
  {
    log("SYSERR: no valid target to act()!");
    return;
  }

  if (ch && IS_ASTRAL(ch) && !hide_invisible)
    hide_invisible = TRUE;

  if ( type == TO_ROLLS )
  {
    for (; to; to = to->next_in_room) {
      if (!PRF_FLAGGED(to, PRF_ROLLS))
        continue;
      if (SENDOK(to)
          && !(hide_invisible
               && ch
               && !CAN_SEE(to, ch)))
        perform_act(str, ch, obj, vict_obj, to);
    }
    return;
  }

  for (; to; to = to->next_in_room)
  {
    if (SENDOK(to)
        && !(hide_invisible
             && ch
             && !CAN_SEE(to, ch))
        && (to != ch) && !(PLR_FLAGGED(to, PLR_REMOTE) || PLR_FLAGGED(to, PLR_MATRIX))
        && (type == TO_ROOM || type == TO_ROLLS || (to != vict_obj)))
      perform_act(str, ch, obj, vict_obj, to);
  }
}
#undef SENDOK