text: /* ************************************************************************
  *   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_C__
  
  #include "conf.h"
  #include "sysdep.h"
  #include "screen.h"
  
  #ifdef CIRCLE_WINDOWS		/* Includes for Win32 */
  # include <direct.h>
  # include <mmsystem.h>
  #endif /* CIRCLE_WINDOWS */
  
  #ifdef CIRCLE_AMIGA		/* Includes for the Amiga */
  # include <sys/socket.h>
  # include <netinet/in.h>
  # include <netdb.h>
  # include <fcntl.h>
  # include <sys/ioctl.h>
  # include <sys/fcntl.h>
  # include <clib/socket_protos.h>
  #endif /* CIRCLE_AMIGA */
  
  #ifdef CIRCLE_OS2		/* Includes for OS/2 */
  # include <sys/wait.h>
  # include <fcntl.h>
  # include <sys/fcntl.h>
  # include <sys/select.h>
  # include <sys/socket.h>
  # include <sys/resource.h>
  # include <netinet/in.h>
  # include <netdb.h>
  #endif /* CIRCLE_OS2 */
  
  /* Note, includes for UNIX are in sysdep.h */
  
  #include "structs.h"
  #include "utils.h"
  #include "comm.h"
  #include "interpreter.h"
  #include "handler.h"
  #include "db.h"
  #include "house.h"
  #include "olc.h"
  #include "dg_scripts.h"
  #include "spells.h"
  #include "boards.h"
  
  #ifdef HAVE_ARPA_TELNET_H
  #include <arpa/telnet.h>
  #else
  #include "telnet.h"
  #endif
  
  #ifndef INVALID_SOCKET
  #define INVALID_SOCKET -1
  #endif
  
  /* externs */
  extern struct ban_list_element *ban_list;
  extern int num_invalid;
  extern char *GREETINGS;
  extern const char *circlemud_version;
  extern int circle_restrict;
  extern int mini_mud;
  extern int no_rent_check;
  extern FILE *player_fl;
  extern ush_int DFLT_PORT;
  extern const char *DFLT_DIR;
  extern const char *DFLT_IP;
  extern const char *LOGNAME;
  extern int max_playing;
  extern int nameserver_is_slow; /* see config.c */
  extern int auto_save;          /* see config.c */
  extern int autosave_time;      /* see config.c */
  extern int xap_objs;		/* ascii objects. */
  extern int MAX_DESCRIPTORS_AVAILABLE;
  
  extern struct room_data *world;	/* In db.c */
  extern struct time_info_data time_info;		/* In db.c */
  extern char *help;
  
  /* unknown globals */
  extern int top_of_zone_table;
  extern room_rnum top_of_world;
  extern struct zone_data *zone_table;
  extern const char *save_info_msg[];	/* In olc.c */
  extern void proc_color(char *inbuf, int color);
  
  /* 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 */
  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 */
  int dg_act_check;               /* toggle for act_trigger */
  unsigned long dg_global_pulse = 0; /* number of pulses since game start */
  struct timeval null_time;	/* zero-valued time structure */
  FILE *logfile = NULL;
  
  /* functions in this file */
  RETSIGTYPE reread_wizlists(int sig);
  RETSIGTYPE unrestrict_game(int sig);
  RETSIGTYPE reap(int sig);
  RETSIGTYPE checkpointing(int sig);
  RETSIGTYPE hupsig(int sig);
  ssize_t perform_socket_read(socket_t desc, char *read_point, size_t space_left);
  ssize_t perform_socket_write(socket_t desc, const char *txt, size_t length);
  void echo_off(struct descriptor_data *d);
  void echo_on(struct descriptor_data *d);
  void sanity_check(void);
  void circle_sleep(struct timeval *timeout);
  int get_from_q(struct txt_q *queue, char *dest, int *aliased);
  void init_game(ush_int port);
  void signal_setup(void);
  void check_fishing();
  void check_digging();
  void game_loop(int mother_desc);
  int init_socket(ush_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 timediff(struct timeval *diff, struct timeval *a, struct timeval *b);
  void timeadd(struct timeval *sum, struct timeval *a, struct timeval *b);
  void flush_queues(struct descriptor_data *d);
  void nonblock(socket_t 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);
  char *make_prompt(struct descriptor_data *point);
  void check_idle_passwords(void);
  void heartbeat(int pulse);
  struct in_addr *get_bind_addr(void);
  int parse_ip(const char *addr, struct in_addr *inaddr);
  int set_sendbuf(socket_t s);
  void setup_log(const char *filename, int fd);
  int open_logfile(const char *filename, FILE *stderr_fp);
  #if defined(POSIX)
  sigfunc *my_signal(int signo, sigfunc *func);
  #endif
  void close_socket(struct descriptor_data *d);
  
  /* extern fcnts */
  void process_events(void);
  void reboot_wizlists(void);
  void boot_world(void);
  void affect_update(void);	/* In spells.c */
  void mobile_activity(void);
  void perform_violence(void);
  void show_string(struct descriptor_data *d, char *input);
  int isbanned(char *hostname);
  void weather_and_time(int mode);
  int level_exp(int class, int level);
  void boot_db(void);
  void zone_update(void);
  void point_update(void);	/* In limits.c */
  void ten_second_update(void);	/* In limits.c */
  void string_add(struct descriptor_data *d, char *str);
  void perform_pulse_damage(void);
  void perform_area_magic(void);
  void redit_save_to_disk(int zone_num);
  void oedit_save_to_disk(int zone_num);
  void medit_save_to_disk(int zone_num);
  void sedit_save_to_disk(int zone_num);
  void zedit_save_to_disk(int zone_num);
  int real_zone(int number);
  
  void die_follower(struct char_data *ch);
  
  // Copyover
  static bool fCopyOver;          /* Are we booting in copyover mode? */
  ush_int port;
  socket_t mother_desc;
  void init_descriptor (struct descriptor_data *newd, int desc);
  void copyover_recover(void);
  
  #ifdef __CXREF__
  #undef FD_ZERO
  #undef FD_SET
  #undef FD_ISSET
  #undef FD_CLR
  #define FD_ZERO(x)
  #define FD_SET(x, y) 0
  #define FD_ISSET(x, y) 0
  #define FD_CLR(x, y)
  #endif
  
  void update_blood(void)
  {
    room_rnum i;
  
    for (i = 0; i < top_of_world; i++)
      if (RM_BLOOD(i) > 0)
        RM_BLOOD(i) -= 1;
  }
  
  /***********************************************************************
  *  main game loop and related stuff                                    *
  ***********************************************************************/
  
  /* Windows doesn't have gettimeofday, so we'll simulate it. */
  #ifdef CIRCLE_WINDOWS
  
  void gettimeofday(struct timeval *t, struct timezone *dummy)
  {
    DWORD millisec = GetTickCount();
  
    t->tv_sec = (int) (millisec / 1000);
    t->tv_usec = (millisec % 1000) * 1000;
  }
  
  #endif
  
  #define plant_magic(x)  do { (x)[sizeof(x) - 1] = MAGIC_NUMBER; } while (0)
  #define test_magic(x)   ((x)[sizeof(x) - 1])
  
  int main(int argc, char **argv)
  {
    ush_int port;
    int pos = 1;
    const char *dir;
  
    /* Initialize these to check for overruns later. */
    plant_magic(buf);
    plant_magic(buf1);
    plant_magic(buf2);
    plant_magic(arg);
  
    port = DFLT_PORT;
    dir = DFLT_DIR;
  
    while ((pos < argc) && (*(argv[pos]) == '-')) {
      switch (*(argv[pos] + 1)) {
      case 'l':
        if (*(argv[pos] + 2))
          LOGNAME = argv[pos] + 2;
        else if (++pos < argc)
          LOGNAME = argv[pos];
        else {
          puts("SYSERR: File name to log to expected after option -o.");
          exit(1);
        }
        break;
      case 'C': /* -C<socket number> - recover from copy over, this is the control socket */
         fCopyOver = TRUE;
         mother_desc = atoi(argv[pos]+2);
        break;			
      case 'd':
        if (*(argv[pos] + 2))
  	dir = argv[pos] + 2;
        else if (++pos < argc)
  	dir = argv[pos];
        else {
  	puts("SYSERR: Directory arg expected after option -d.");
  	exit(1);
        }
        break;
      case 'm':
        mini_mud = 1;
        no_rent_check = 1;
        puts("Running in minimized mode & with no rent check.");
        break;
      case 'c':
        scheck = 1;
        puts("Syntax check mode enabled.");
        break;
      case 'q':
        no_rent_check = 1;
        puts("Quick boot mode -- rent check supressed.");
        break;
      case 'r':
        circle_restrict = 1;
        puts("Restricting game -- no new players allowed.");
        break;
      case 's':
        no_specials = 1;
        puts("Suppressing assignment of special routines.");
        break;
      case 'o':
        xap_objs = 1;
        puts("Loading player objects from secondary (ascii) files.");		
        break;	
      case 'h':
        /* From: Anil Mahajan <amahajan@proxicom.com> */
        /* Do NOT use -C, this is the copyover mode and without
                    * the proper copyover.dat file, the game will go nuts!
                    * -spl */			
        printf("Usage: %s [-c] [-o] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n"
                "  -c             Enable syntax check mode.\n"
                "  -d <directory> Specify library directory (defaults to 'lib').\n"
                "  -h             Print this command line argument help.\n"
                "  -m             Start in mini-MUD mode.\n"
                "  -o <file>      Write log to <file> instead of stderr.\n"
                "  -q             Quick boot (doesn't scan rent for object limits)\n"
                "  -r             Restrict MUD -- no new players allowed.\n"
                "  -s             Suppress special procedure assignments.\n"
                " Note:		These arguments are 'CaSe SeNsItIvE!!!'\n",
                   argv[0]
        );
        exit(0);
      default:
        printf("SYSERR: Unknown option -%c in argument string.\n", *(argv[pos] + 1));
        break;
      }
      pos++;
    }
  
    if (pos < argc) {
      if (!isdigit(*argv[pos])) {
        printf("Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n", argv[0]);
        exit(1);
      } else if ((port = atoi(argv[pos])) <= 1024) {
        printf("SYSERR: Illegal port number %d.\n", port);
        exit(1);
      }
    }
  
    /* All arguments have been parsed, try to open log file. */
    setup_log(LOGNAME, STDERR_FILENO);
  
    /*
     * Moved here to distinguish command line options and to show up
     * in the log if stderr is redirected to a file.
     */
    log(circlemud_version);
  
    if (chdir(dir) < 0) {
      perror("SYSERR: Fatal error changing to data directory");
      exit(1);
    }
    log("Using %s as data directory.", dir);
  
    if (scheck) {
      boot_world();
      log("Done.");
    } else {
      log("Running game on port %d.", port);
      init_game(port);
    }
    return 0;
  }
  
  
  /* Init sockets, run game, and cleanup sockets */
  void init_game(ush_int port)
  {
    /* We don't want to restart if we crash before we get up. */
    touch(KILLSCRIPT_FILE);
  
    circle_srandom(time(0));
  
    log("Finding player limit.");
    max_players = get_max_players();
  
    /* If copyover mother_desc is already set up */
    if (!fCopyOver) {
       log ("Opening mother connection.");
       mother_desc = init_socket (port);
    }	
  	
    boot_db();
  
  #ifdef CIRCLE_UNIX
    log("Signal trapping.");
    signal_setup();
  #endif
  
    /* If we made it this far, we will be able to restart without problem. */
    remove(KILLSCRIPT_FILE);
  
    if (fCopyOver) /* reload players */
    copyover_recover();
  
  	
    log("Entering game loop.");
  
    game_loop(mother_desc);
  
    Crash_save_all();
  
    log("Closing all sockets.");
    while (descriptor_list)
      close_socket(descriptor_list);
  
    CLOSE_SOCKET(mother_desc);
    fclose(player_fl);
  
    if (circle_reboot != 2 && olc_save_list) { /* Don't save zones. */
      struct olc_save_info *entry, *next_entry;
      int rznum;
  
      for (entry = olc_save_list; entry; entry = next_entry) {
        next_entry = entry->next;
        if (entry->type < 0 || entry->type > 4) {
          sprintf(buf, "OLC: Illegal save type %d!", entry->type);
          log(buf);
        } else if ((rznum = real_zone(entry->zone * 100)) == -1) {
          sprintf(buf, "OLC: Illegal save zone %d!", entry->zone);
          log(buf);
        } else if (rznum < 0 || rznum > top_of_zone_table) {
  	sprintf(buf, "OLC: Invalid real zone number %d!", rznum);
  	log(buf);
        } else {
          sprintf(buf, "OLC: Reboot saving %s for zone %d.",
  		save_info_msg[(int)entry->type], zone_table[rznum].number);
          log(buf);
          switch (entry->type) {
          case OLC_SAVE_ROOM: redit_save_to_disk(rznum); break;
          case OLC_SAVE_OBJ:  oedit_save_to_disk(rznum); break;
          case OLC_SAVE_MOB:  medit_save_to_disk(rznum); break;
          case OLC_SAVE_ZONE: zedit_save_to_disk(rznum); break;
          case OLC_SAVE_SHOP: sedit_save_to_disk(rznum); break;
          default:      log("Unexpected olc_save_list->type"); break;
          }
        }
      }
    }
  
    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(ush_int port)
  {
    int s, opt;
    struct sockaddr_in sa;
  
  #ifdef CIRCLE_WINDOWS
    {
      WORD wVersionRequested;
      WSADATA wsaData;
  
      wVersionRequested = MAKEWORD(1, 1);
  
      if (WSAStartup(wVersionRequested, &wsaData) != 0) {
        log("SYSERR: WinSock not available!\n");
        exit(1);
      }
      if ((wsaData.iMaxSockets - 4) < max_players) {
        max_players = wsaData.iMaxSockets - 4;
      }
      sprintf(buf, "Max players set to %d", max_players);
      log(buf);
  
      if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        fprintf(stderr, "SYSERR: Error opening network connection: Winsock err #%d\n", WSAGetLastError());
        exit(1);
      }
    }
  #else
    /*
     * 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 the point is (hopefully) moot.
     */
  
    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
      perror("SYSERR: Error creating socket");
      exit(1);
    }
  #endif				/* CIRCLE_WINDOWS */
  
  #if defined(SO_REUSEADDR)
    opt = 1;
    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) {
      perror("SYSERR: setsockopt REUSEADDR");
      exit(1);
    }
  #endif
  
    set_sendbuf(s);
  
  #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("SYSERR: setsockopt SO_LINGER");
      }
    }
  #endif
  
    /* Clear the structure */
    memset((char*) &sa, 0, sizeof(sa));
  
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr = *(get_bind_addr());
  
    if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
      perror("SYSERR: bind");
      CLOSE_SOCKET(s);
      exit(1);
    }
    nonblock(s);
    listen(s, 5);
    return(s);
  }
  
  
  int get_max_players(void)
  {
  #ifndef CIRCLE_UNIX
    return (max_playing);
  #else
  
    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;
  
      /* find the limit of file descs */
      method = "rlimit";
      if (getrlimit(RLIMIT_NOFILE, &limit) < 0) {
        perror("SYSERR: calling getrlimit");
        exit(1);
      }
      /* set the current to the maximum */
      limit.rlim_cur = limit.rlim_max;
      if (setrlimit(RLIMIT_NOFILE, &limit) < 0) {
        perror("SYSERR: calling setrlimit");
        exit(1);
      }
  #ifdef RLIM_INFINITY
      if (limit.rlim_max == RLIM_INFINITY)
        max_descs = max_playing + NUM_RESERVED_DESCS;
      else
        max_descs = MIN(max_playing + NUM_RESERVED_DESCS, limit.rlim_max);
  #else
      max_descs = MIN(max_playing + NUM_RESERVED_DESCS, limit.rlim_max);
  #endif
    }
  
  #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 */
  #elif defined (_SC_OPEN_MAX)
    /*
     * 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_playing + NUM_RESERVED_DESCS;
      else {
        perror("SYSERR: Error calling sysconf");
        exit(1);
      }
    }
  #else
    /* if everything has failed, we'll just take a guess */
    method = "random guess";
    max_descs = max_playing + NUM_RESERVED_DESCS;
  #endif
  
    /* now calculate max _players_ based on max descs */
    max_descs = MIN(max_playing, max_descs - NUM_RESERVED_DESCS);
  
    if (max_descs <= 0) {
      log("SYSERR: 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);
  #endif /* CIRCLE_UNIX */
  }
  
  
  
  /*
   * 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, null_set;
    struct timeval last_time, opt_time, process_time, temp_time;
    struct timeval before_sleep, now, timeout;
    char comm[MAX_INPUT_LENGTH];
    struct descriptor_data *d, *next_d;
    int pulse = 0, missed_pulses, maxdesc, aliased;
  
    /* int PDHtimecheck = 0; */
  
    /* initialize various time values */
    null_time.tv_sec = 0;
    null_time.tv_usec = 0;
    opt_time.tv_usec = OPT_USEC;
    opt_time.tv_sec = 0;
    FD_ZERO(&null_set);
  
    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) {
        log("No connections.  Going to sleep.");
        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) {
  #ifndef CIRCLE_WINDOWS
        if (d->descriptor > maxdesc)
  	maxdesc = d->descriptor;
  #endif
        FD_SET(d->descriptor, &input_set);
        FD_SET(d->descriptor, &output_set);
        FD_SET(d->descriptor, &exc_set);
      }
  
        /*
         * At this point, we have completed all input, output and heartbeat
         * activity from the previous iteration, so we have to put ourselves
         * to sleep until the next 0.1 second tick.  The first step is to
         * calculate how long we took processing the previous iteration.
         */
  
      gettimeofday(&before_sleep, (struct timezone *) 0); /* current time */
      timediff(&process_time, &before_sleep, &last_time);
  
      /*
       * If we were asleep for more than one pass, count missed pulses and sleep
       * until we're resynchronized with the next upcoming pulse.
       */
      if (process_time.tv_sec == 0 && process_time.tv_usec < OPT_USEC) {
        missed_pulses = 0;
      } else {
        missed_pulses = process_time.tv_sec * PASSES_PER_SEC;
        missed_pulses += process_time.tv_usec / OPT_USEC;
        process_time.tv_sec = 0;
        process_time.tv_usec = process_time.tv_usec % OPT_USEC;
      }
  
      /* Calculate the time we should wake up */
      timediff(&temp_time, &opt_time, &process_time);
      timeadd(&last_time, &before_sleep, &temp_time);
  
      /* Now keep sleeping until that time has come */
      gettimeofday(&now, (struct timezone *) 0);
      timediff(&timeout, &last_time, &now);
  
  #if 0
      /* Go to sleep */
  PDHtimecheck = 0;
      do {
  #ifdef CIRCLE_WINDOWS
        Sleep(timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
  #else
        if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
  	if (errno != EINTR) {
  	  perror("SYSERR: Select sleep");
  	  exit(1);
  	}
        }
  #endif /* CIRCLE_WINDOWS */
        gettimeofday(&now, (struct timezone *) 0);
        /* timeout = timediff(last_time, now); */
  if ( (now.tv_sec > last_time.tv_sec) ||
       ( (now.tv_sec == last_time.tv_sec) && (now.tv_usec > last_time.tv_usec) ) ) {
  PDHtimecheck = 1;
  }
  
      } while ( (timeout.tv_usec || timeout.tv_sec) && (PDHtimecheck < 1) );
  #else
      /* Go to sleep */
      do {
        circle_sleep(&timeout);
        gettimeofday(&now, (struct timezone *) 0);
        timediff(&timeout, &last_time, &now);
      } while (timeout.tv_usec || timeout.tv_sec);
  #endif
  
      /* Poll (without blocking) for new input, output, and exceptions */
      if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) <0){
        perror("Select poll");
        return;
      }
      /* If there are new connections waiting, accept them. */
      if (FD_ISSET(mother_desc, &input_set))
        new_descriptor(mother_desc);
  
      /* Kick out the freaky folks in the exception set and marked for close */
      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 commands we just read from process_input */
      for (d = descriptor_list; d; d = next_d) {
        next_d = d->next;
  
        /*
         * Not combined to retain --(d->wait) behavior. -gg 2/20/98
         * If no wait state, no subtraction.  If there is a wait
         * state then 1 is subtracted. Therefore we don't go less
         * than 0 ever and don't require an 'if' bracket. -gg 2/27/99
         */
        if (d->character) {
          GET_WAIT_STATE(d) -= (GET_WAIT_STATE(d) > 0);
  
          if (GET_WAIT_STATE(d))
            continue;
        }
  
        if (!get_from_q(&d->input, comm, &aliased))
          continue;
  
       if (d->character) {
          /* Reset the idle timer & pull char back from void if necessary */
          d->character->char_specials.timer = 0;
          if (STATE(d) == CON_PLAYING && 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);
          }
          GET_WAIT_STATE(d) = 1;
        }
        d->has_prompt = 0;
        if (d->str)               /* Writing boards, mail, etc. */
          string_add(d, comm);
        else if (d->showstr_count) /* Reading something w/ pager */
          show_string(d, comm);
        else if (STATE(d) != CON_PLAYING) { /* In menus, etc. */
  	    //SEND_TO_Q("NANNY SEQUENCE ENTERED\r\n", d);
          nanny(d, comm);
          }
        else {                    /* else: we're playing normally. */
          if (aliased)            /* To prevent recursive aliases. */
            d->has_prompt = 1;    /* To get newline before next cmd output. */
          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 (*(d->output) && FD_ISSET(d->descriptor, &output_set)) {
  	/* Output for this player is ready */
  	if (process_output(d) < 0)
  	  close_socket(d);
  	else
  	  d->has_prompt = 1;
        }
      }
  
      /* Print prompts for other descriptors who had no other output */
      for (d = descriptor_list; d; d = d->next) {
        if (!d->has_prompt) {
  	write_to_descriptor(d->descriptor, make_prompt(d));
  	d->has_prompt = 1;
        }
      }
  
      /* Kick out folks in the CON_CLOSE or CON_DISCONNECT state */
      for (d = descriptor_list; d; d = next_d) {
        next_d = d->next;
        if (STATE(d) == CON_CLOSE || STATE(d) == CON_DISCONNECT) {
  	close_socket(d);
  
        }
      }
  
      /*
       * Now, we execute as many pulses as necessary--just one if we haven't
       * missed any pulses, or make up for lost time if we missed a few
       * pulses by sleeping for too long.
       */
      missed_pulses++;
  
      if (missed_pulses <= 0) {
        log("SYSERR: **BAD** MISSED_PULSES NONPOSITIVE (%d), TIME GOING BACKWARDS!!", missed_pulses);
        missed_pulses = 1;
      }
  
      /* If we missed more than 30 seconds worth of pulses, just do 30 secs */
      if (missed_pulses > (30 * PASSES_PER_SEC)) {
        log("SYSERR: Missed %d seconds worth of pulses", missed_pulses / PASSES_PER_SEC);
        missed_pulses = 30 * PASSES_PER_SEC;
      }
  
      /* Now execute the heartbeat functions */
      while (missed_pulses--)
        heartbeat(++pulse);
  
      /* Roll pulse over after 10 hours */
      if (pulse >= (600 * 60 * PASSES_PER_SEC))
        pulse = 0;
  
  #ifdef CIRCLE_UNIX
      /* Update tics for deadlock protection (UNIX only) */
      tics++;
  #endif
    }
  }
  
  
  void heartbeat(int pulse)
  {
    static int mins_since_crashsave = 0;
  
    dg_global_pulse++;
  
    process_events();
  
    if (!(pulse % PULSE_DG_SCRIPT)) {
        script_trigger_check();
    }
  
    if (!(pulse % (30 * PASSES_PER_SEC)))
      sanity_check();
  
    if (!(pulse % PULSE_ZONE)) {
      zone_update();
    }
  
    if (!(pulse % (15 * PASSES_PER_SEC))) {       /* 15 seconds */
      check_idle_passwords();
    }
  
    if (!(pulse % PULSE_MOBILE)) {
        //mobile_activity();
    }
  
    if (!(pulse % PULSE_VIOLENCE)) {
      perform_violence();
      perform_pulse_damage();
      perform_area_magic();
    }
  
     if (!(pulse % (40 * PASSES_PER_SEC)))
      check_fishing();
  
     if (!(pulse % (40 * PASSES_PER_SEC))) {
      check_digging();
     }
  
    if (!(pulse % (10 * PASSES_PER_SEC)))
      ten_second_update();
  
    if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) {
      update_blood();
      weather_and_time(1);
      affect_update();
      point_update();
      fflush(player_fl);
    }
  
    if (auto_save && !(pulse % (60 * PASSES_PER_SEC))) {	/* 1 minute */
      if (++mins_since_crashsave >= autosave_time) {
        mins_since_crashsave = 0;
        Crash_save_all_II();
        /*House_save_all(); */
        auto_backup_equipment();  
      }
    }
  
    if (!(pulse % (5 * 60 * PASSES_PER_SEC))) {    /* 5 minutes */
      record_usage();
    }
  }
  
  
  /* ******************************************************************
  *  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).
   */
  void timediff(struct timeval *rslt, struct timeval *a, struct timeval *b)
  {
    if (a->tv_sec < b->tv_sec)
      *rslt = null_time;
    else if (a->tv_sec == b->tv_sec) {
      if (a->tv_usec < b->tv_usec)
        *rslt = null_time;
      else {
        rslt->tv_sec = 0;
        rslt->tv_usec = a->tv_usec - b->tv_usec;
      }
    } 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;
    }
  }
  
  /* add 2 timevals */
  void timeadd(struct timeval *rslt, struct timeval *a, struct timeval *b)
  {
    rslt->tv_sec = a->tv_sec + b->tv_sec;
    rslt->tv_usec = a->tv_usec + b->tv_usec;
  
    while (rslt->tv_usec >= 1000000) {
      rslt->tv_usec -= 1000000;
      rslt->tv_sec++;
    }
  }
  
  
  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 (STATE(d) == CON_PLAYING)
        sockets_playing++;
    }
  
    log("nusage: %-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)
  {
    static 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)
  {
    static char on_string[] =
    {
      (char) IAC,
      (char) WONT,
      (char) TELOPT_ECHO,
      (char) 0,
    };
  
    SEND_TO_Q(on_string, d);
  }
  
  
  void short_diagnose(struct char_data * i, struct char_data *fr,
    char retBuf[MAX_INPUT_LENGTH])
  {
    int  percent;
  
    memset(retBuf, (char) NULL, MAX_INPUT_LENGTH);
  
    if (GET_MAX_HIT(i) > 0) {
      percent = (100 * GET_HIT(i)) / GET_MAX_HIT(i);
    } else {
      percent = -1;		/* How could MAX_HIT be < 1?? */
    }
  
    if ( ! CAN_SEE(fr, i) ) {
      strncpy(retBuf, "someone: ???", MAX_INPUT_LENGTH - 1);
    } else {
     CHOOSE_NAME_II(i, fr, chname);
      strncpy(retBuf, chname, MAX_INPUT_LENGTH - 1);
      FREE_NAME(chname);
      CAP(retBuf);
  
      if (percent >= 100)
        strcat(retBuf, ": superb");
      else if (percent >= 90)
        strcat(retBuf, ": few scratches");
      else if (percent >= 80)
        strcat(retBuf, ": nicks and cuts");
      else if (percent >= 70)
        strcat(retBuf, ": bleeding slightly");
      else if (percent >= 60)
        strcat(retBuf, ": light wounds");
      else if (percent >= 50)
        strcat(retBuf, ": moderate wounds");
      else if (percent >= 40)
        strcat(retBuf, ": wounded");
      else if (percent >= 30)
        strcat(retBuf, ": heavy wounds");
      else if (percent >= 20)
        strcat(retBuf, ": severe wounds");
      else if (percent >= 10)
        strcat(retBuf, ": fading away");
      else if (percent >= 0)
        strcat(retBuf, ": near death");
      else
        strcat(retBuf, ": brink of death ");
    }
  }
  
  
  char *make_prompt(struct descriptor_data *d)
  {
    static char prompt[MAX_PROMPT_LENGTH + 1];
  
    /* Note, prompt is truncated at MAX_PROMPT_LENGTH chars (structs.h )*/
  
    memset(prompt, (char) NULL, MAX_PROMPT_LENGTH + 1);
  
    if (d->str)
      strcpy(prompt, "] ");
    else if (d->showstr_count) {
      sprintf(prompt,
  	    "\r[ Return to continue, (q)uit, (r)efresh, (b)ack, or page number (%d/%d) ]",
  	    d->showstr_page, d->showstr_count);
  
    } else if (STATE(d) == CON_PLAYING && !IS_NPC(d->character)) {
      int count = 0;
      struct char_data *attackee, *opponent;
      char fightProm[MAX_INPUT_LENGTH];
      char retBuf[MAX_INPUT_LENGTH];
  
      *fightProm = '\0';
  
      if ( d != NULL ) {
        if ( d->original != NULL ) {
  	if ( d->original->char_specials.position == POS_FIGHTING ) {
  
  	  attackee = d->original->char_specials.fighting;
  
  	  if ( attackee != NULL ) {
  	    opponent = FIGHTING(attackee);
  
  	    if ( opponent != NULL ) {
  	      short_diagnose(attackee, d->original, retBuf);
  	      count += sprintf(fightProm, "%s  ", retBuf);
  	      short_diagnose(opponent, d->original, retBuf);
  	      count += sprintf(fightProm, "%s%s ", fightProm, retBuf);
  	    }
  	  }
  	}
        } else if ( d->character != NULL ) {
  	if ( d->character->char_specials.position == POS_FIGHTING ) {
  	  attackee = d->character->char_specials.fighting;
  
  	  if ( attackee != NULL ) {
  	    opponent = FIGHTING(attackee);
  
  	    if ( opponent != NULL ) {
  	      short_diagnose(attackee, d->character, retBuf);
  	      count += sprintf(fightProm, "%s  ", retBuf);
  	      short_diagnose(opponent, d->character, retBuf);
  	      count += sprintf(fightProm, "%s%s ", fightProm, retBuf);
  	    }
  	  }
  
  	}
        }
      }
  
  
      *prompt = '\0';
  
      if (GET_INVIS_LEV(d->character))
        count += sprintf(prompt, "i%d ", GET_INVIS_LEV(d->character));
  
      if (PRF_FLAGGED(d->character, PRF_DISPHP))
        count += sprintf(prompt, "%s%dH ", prompt, GET_HIT(d->character));
  
      if (PRF_FLAGGED(d->character, PRF_DISPMOVE))
        count += sprintf(prompt, "%s%dV ", prompt, GET_MOVE(d->character));
  
  // New exp % code by Gicker
     int int_xp = 0;
     int int_percent;
     float percent = 0.0;
     float xp = (((float) GET_EXP(d->character)) - ((float) level_exp(GET_CLASS(d->character), 
  	            GET_LEVEL(d->character)))) /
                (((float) level_exp(GET_CLASS(d->character), (GET_LEVEL(d->character) + 1)) -
                (float) level_exp(GET_CLASS(d->character), GET_LEVEL(d->character))));
  	
     if (((float) GET_EXP(d->character) - (float) level_exp(GET_CLASS(d->character), 
  	              GET_LEVEL(d->character))) < 0)	{
  	
  	  xp = (((float) level_exp(GET_CLASS(d->character), GET_LEVEL(d->character))) - 
  		     (float) GET_EXP(d->character)) /
  	     ((float) level_exp(GET_CLASS(d->character), (GET_LEVEL(d->character) + 1)) -
                (float) level_exp(GET_CLASS(d->character), GET_LEVEL(d->character)));
      
  	  xp *= (float) 1000.0;
  	  percent = (int) xp % 10;
  	  xp /= (float) 10;
  	  int_xp = MAX(0, (int) xp);
  	  int_percent = MAX(0, MIN((int) percent, 99));
  	  
  	  int_xp = int_xp - (int_xp * 2);
  	
     }
     else {
  	   xp *= (float) 1000.0;
  	   percent = (int) xp % 10;
  	   xp /= (float) 10;
  	   int_xp = MAX(0, (int) xp);
  	   int_percent = MAX(0, MIN((int) percent, 99));   
     }			
  			
    if (!FIGHTING(d->character))
      count += sprintf(prompt, "%s[%d%% Tnl] ", prompt, int_xp);
        
  
      strcat(prompt, fightProm);
      strcat(prompt, "> ");
  
    } else if (STATE(d) == CON_PLAYING && IS_NPC(d->character)) {
    CHOOSE_NAME(d->character, chname);
      sprintf(prompt, "%s> ", chname);
      FREE_NAME(chname);
   } else
      *prompt = '\0';
  
    return (prompt);
  }
  
  
  void write_to_q(const char *txt, struct txt_q *queue, int aliased)
  {
    struct txt_block *newt;
  
    CREATE(newt, struct txt_block, 1);
    newt->text = str_dup(txt);
    newt->aliased = aliased;
  
    /* queue empty? */
    if (!queue->head) {
      newt->next = NULL;
      queue->head = queue->tail = newt;
    } else {
      queue->tail->next = newt;
      queue->tail = newt;
      newt->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;
  
    free(tmp->text);
    free(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;
  
    /* if we're in the overflow state already, ignore this new output */
    if (t->bufptr < 0)
      return;
  
    size = strlen(txt);
  
    /* 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 the text is too big to fit into even a large buffer, chuck the
     * new text and switch to the overflow state.
     */
    if (size + t->bufptr > LARGE_BUFSIZE - 1) {
      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 */
      CREATE(t->large_outbuf, struct txt_block, 1);
      CREATE(t->large_outbuf->text, 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 */
  
    /* set the pointer for the next write */
    t->bufptr = strlen(t->output);
  
    /* calculate how much space is left in the buffer */
    t->bufspace = LARGE_BUFSIZE - 1 - t->bufptr;
  }
  
  
  
  /* ******************************************************************
  *  socket handling                                                  *
  ****************************************************************** */
  
  
  /*
   * get_bind_addr: Return a struct in_addr that should be used in our
   * call to bind().  If the user has specified a desired binding
   * address, we try to bind to it; otherwise, we bind to INADDR_ANY.
   * Note that inet_aton() is preferred over inet_addr() so we use it if
   * we can.  If neither is available, we always bind to INADDR_ANY.
   */
  
  struct in_addr *get_bind_addr()
  {
    static struct in_addr bind_addr;
  
    /* Clear the structure */
    memset((char *) &bind_addr, 0, sizeof(bind_addr));
  
    /* If DLFT_IP is unspecified, use INADDR_ANY */
    if (DFLT_IP == NULL) {
      bind_addr.s_addr = htonl(INADDR_ANY);
    } else {
      /* If the parsing fails, use INADDR_ANY */
      if (!parse_ip(DFLT_IP, &bind_addr)) {
        log("SYSERR: DFLT_IP of %s appears to be an invalid IP address",DFLT_IP);
        bind_addr.s_addr = htonl(INADDR_ANY);
      }
    }
  
    /* Put the address that we've finally decided on into the logs */
    if (bind_addr.s_addr == htonl(INADDR_ANY))
      log("Binding to all IP interfaces on this host.");
    else
      log("Binding only to IP address %s", inet_ntoa(bind_addr));
  
    return (&bind_addr);
  }
  
  #ifdef HAVE_INET_ATON
  
  /*
   * inet_aton's interface is the same as parse_ip's: 0 on failure, non-0 if
   * successful
   */
  int parse_ip(const char *addr, struct in_addr *inaddr)
  {
    return (inet_aton(addr, inaddr));
  }
  
  #elif HAVE_INET_ADDR
  
  /* inet_addr has a different interface, so we emulate inet_aton's */
  int parse_ip(const char *addr, struct in_addr *inaddr)
  {
    long ip;
  
    if ((ip = inet_addr(addr)) == -1) {
      return (0);
    } else {
      inaddr->s_addr = (unsigned long) ip;
      return (1);
    }
  }
  
  #else
  
  /* If you have neither function - sorry, you can't do specific binding. */
  int parse_ip(const char *addr, struct in_addr *inaddr)
  {
    log("SYSERR: warning: you're trying to set DFLT_IP but your system has no\n"
        "functions to parse IP addresses (how bizarre!)");
    return (0);
  }
  
  #endif /* INET_ATON and INET_ADDR */
  
  /* Sets the kernel's send buffer size for the descriptor */
  int set_sendbuf(socket_t s)
  {
  #if defined(SO_SNDBUF)
    int opt = MAX_SOCK_BUF;
  
    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) {
      perror("SYSERR: setsockopt SNDBUF");
      return -1;
    }
  
  #if 0
    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) {
      perror("SYSERR: setsockopt RCVBUF");
      return -1;
    }
  #endif
  
  #endif
  
    return (0);
  }
  
  int new_descriptor(int s) {
    socket_t desc;
    int sockets_connected = 0;
    socklen_t i;
    struct descriptor_data *newd;
    struct sockaddr_in peer;
    struct hostent *from;
  
    /* accept the new connection */
    i = sizeof(peer);
    if ((desc = accept(s, (struct sockaddr *) &peer, &i)) == INVALID_SOCKET) {
      perror("accept");
      return -1;
    }
    /* keep it from blocking */
    nonblock(desc);
  
    /* set the send buffer size if available on the system */
    if (set_sendbuf(desc) < 0) {
      CLOSE_SOCKET(desc);
      return 0;
    }
  
    /* 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, CircleMUD is full right now... please try again later!\r\n");
      CLOSE_SOCKET(desc);
      return 0;
    }
    /* create a new descriptor */
    CREATE(newd, struct descriptor_data, 1);
    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))) {
  
      /* resolution failed */
      if (!nameserver_is_slow)
        perror("SYSERR: gethostbyaddr");
  
      /* find the numeric site address */
      strncpy(newd->host, (char*) inet_ntoa(peer.sin_addr), HOST_LENGTH);
      *(newd->host + HOST_LENGTH) = '\0';
    } 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_SOCKET(desc);
      sprintf(buf2, "Connection attempt denied from [%s]", newd->host);
      mudlog(buf2, CMP, LVL_GOD, TRUE);
      free(newd);
      return (0);
    }
  
  #if 0
    /* Log new connections - probably unnecessary, but you may want it */
    sprintf(buf2, "New connection from [%s]", newd->host);
    mudlog(buf2, CMP, LVL_GOD, FALSE);
  #endif
  
    /* initialize descriptor data */
     init_descriptor(newd, desc);
  
    /* prepend to list */
    newd->next = descriptor_list;
    descriptor_list = newd;
  
    SEND_TO_Q(GREETINGS, newd);
  
    return (0);
  }
  
  
  /*
   * Send all of the output that we've accumulated for a player out to
   * the player's descriptor.
   * FIXME - This will be rewritten before 3.1, this code is dumb.
   */
  int process_output(struct descriptor_data *t)
  {
    char i[MAX_SOCK_BUF];
    int result;
  
    memset(i, (char) NULL, MAX_SOCK_BUF);
  
    /* 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 (STATE(t) == CON_PLAYING && t->character && !IS_NPC(t->character) &&
      !PRF_FLAGGED(t->character, PRF_COMPACT))
      strcat(i + 2, "\r\n");
  
    if(t->character)
     proc_color(i, (clr(t->character, C_NRM)));
  
    /* add a prompt */
    strncat(i + 2, make_prompt(t), MAX_PROMPT_LENGTH);
  
    /*
     * now, send the output.  If this is an 'interruption', use the prepended
     * CRLF, otherwise send the straight output sans CRLF.
     */
    if (t->has_prompt)		/* && !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);
  }
  
  
  /* perform_socket_write for all Non-Windows platforms */
  ssize_t perform_socket_write(socket_t desc, const char *txt, size_t length)
  {
    ssize_t result;
  
    result = write(desc, txt, length);
  
    if (result > 0) {
      /* Write was successful. */
      return (result);
    }
  
    if (result == 0) {
      /* This should never happen! */
      log("SYSERR: Huh??  write() returned 0???  Please report this!");
      return (-1);
    }
  
    /*
     * result < 0, so an error was encountered - is it transient?
     * Unfortunately, different systems use different constants to
     * indicate this.
     */
  
  #ifdef EAGAIN           /* POSIX */
    if (errno == EAGAIN)
      return (0);
  #endif
  
  #ifdef EWOULDBLOCK      /* BSD */
    if (errno == EWOULDBLOCK)
      return (0);
  #endif
  
  #ifdef EDEADLK          /* Macintosh */
    if (errno == EDEADLK)
      return (0);
  #endif
  
    /* Looks like the error was fatal.  Too bad. */
    return (-1);
  }
  
  /*
   * write_to_descriptor takes a descriptor, and text to write to the
   * descriptor.  It keeps calling the system-level write() until all
   * the text has been delivered to the OS, or until an error is
   * encountered.
   *
   * Returns:
   *  0  If all is well and good,
   * -1  If an error was encountered, so that the player should be cut off
   */
  int write_to_descriptor(socket_t desc, const char *txt)
  {
    size_t total; 
    ssize_t bytes_written;
  
    total = strlen(txt);
  
    while (total > 0) {
      bytes_written = perform_socket_write(desc, txt, total);
  
      if (bytes_written < 0) {
        /* Fatal error.  Disconnect the player. */
        perror("SYSERR: Write to socket");
        return (-1);
      } else if (bytes_written == 0) {
        /*
         * Temporary failure -- socket buffer full.  For now we'll just
         * cut off the player, but eventually we'll stuff the unsent
         * text into a buffer and retry the write later.  JE 30 June 98.
         */
        log("WARNING: write_to_descriptor: socket write would block, about to close");
        return (-1);
      } else {
        txt += bytes_written;
        total -= bytes_written;
      }
    }
  
    return (0);
  }
  
  
  /*
   * Same information about perform_socket_write applies here. I like
   * standards, there are so many of them. -gg 6/30/98
   */
  ssize_t perform_socket_read(socket_t desc, char *read_point, size_t space_left)
  {
    ssize_t ret;
  
  #if defined(CIRCLE_ACORN)
    ret = recv(desc, read_point, space_left, MSG_DONTWAIT);
  #elif defined(CIRCLE_WINDOWS)
    ret = recv(desc, read_point, space_left, 0);
  #else
    ret = read(desc, read_point, space_left);
  #endif
  
    /* Read was successful. */
    if (ret > 0)
      return (ret);
  
    /* read() returned 0, meaning we got an EOF. */
    if (ret == 0) {
      log("WARNING: EOF on socket read (connection broken by peer)");
      return (-1);
    }
  
    /*
     * read returned a value < 0: there was an error
     */
  
  #if defined(CIRCLE_WINDOWS)     /* Windows */
    if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINTR)
      return (0);
  #else
  
  #ifdef EINTR            /* Interrupted system call - various platforms */
    if (errno == EINTR)
      return (0);
  #endif
  
  #ifdef EAGAIN           /* POSIX */
    if (errno == EAGAIN)
      return (0);
  #endif
  
  #ifdef EWOULDBLOCK      /* BSD */
    if (errno == EWOULDBLOCK)
      return (0);
  #endif /* EWOULDBLOCK */
  
  #ifdef EDEADLK          /* Macintosh */
    if (errno == EDEADLK)
      return (0);
  #endif
  
  #endif /* CIRCLE_WINDOWS */
  
    /*
     * We don't know what happened, cut them off. This qualifies for
     * a SYSERR because we have no idea what happened at this point.
     */
    perror("SYSERR: perform_socket_read: about to lose connection");
    return (-1);
  }
  
  
  /*
   * 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, failed_subst;
    ssize_t bytes_read;
    size_t space_left;
    char *ptr, *read_point, *write_point, *nl_pos = NULL;
    char tmp[MAX_INPUT_LENGTH];
  
    /* 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("WARNING: process_input: about to close connection: input overflow");
        return (-1);
      }
      bytes_read = perform_socket_read(t->descriptor, read_point, space_left);
  
      if (bytes_read < 0) /* Error, disconnect them. */
        return (-1);
      else if (bytes_read == 0)   /* Just blocking, no problems. */
        return (0);
  
      /* 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 /* POSIX_NONBLOCK_BROKEN */
  
    /*
     * 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 > 1) && (ptr < nl_pos); ptr++) {
        if (*ptr == '\b' || *ptr == 127) { /* handle backspacing or delete key */
          if (write_point > tmp) {
            if (*(--write_point) == '$') {
              write_point--;
              space_left += 2;
            } else
              space_left++;
          }
        } else if (isascii(*ptr) && isprint(*ptr)) {
          if ((*(write_point++) = *ptr) == '$') {         /* copy one character */
            *(write_point++) = '$';       /* if it's a $, double it */
            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 == '!' && !(*(tmp + 1)))   /* Redo last command. */
        strcpy(tmp, t->last_input);
      else if (*tmp == '!' && *(tmp + 1)) {
        char *commandln = (tmp + 1);
        int starting_pos = t->history_pos,
            cnt = (t->history_pos == 0 ? HISTORY_SIZE - 1 : t->history_pos - 1);
  
        skip_spaces(&commandln);
        for (; cnt != starting_pos; cnt--) {
          if (t->history[cnt] && is_abbrev(commandln, t->history[cnt])) {
            strcpy(tmp, t->history[cnt]);
            strcpy(t->last_input, tmp);
            SEND_TO_Q(tmp, t); SEND_TO_Q("\r\n", t);
            break;
          }
          if (cnt == 0)   /* At top, loop to bottom. */
            cnt = HISTORY_SIZE;
        }
      } else if (*tmp == '^') {
        if (!(failed_subst = perform_subst(t, t->last_input, tmp)))
          strcpy(t->last_input, tmp);
      } else {
        strcpy(t->last_input, tmp);
        if (t->history[t->history_pos])
          free(t->history[t->history_pos]);       /* Clear the old line. */
        t->history[t->history_pos] = str_dup(tmp);        /* Save the new. */
        if (++t->history_pos >= HISTORY_SIZE)     /* Wrap to top. */
          t->history_pos = 0;
      }
  
      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 newsub[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(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(orig, first))) {
      SEND_TO_Q("Invalid substitution.\r\n", t);
      return 1;
    }
    /* now, we construct the newsub string for output. */
  
    /* first, everything in the original, up to the string to be replaced */
    strncpy(newsub, orig, (strpos - orig));
    newsub[(strpos - orig)] = '\0';
  
    /* now, the replacement string */
    strncat(newsub, second, (MAX_INPUT_LENGTH - strlen(newsub) - 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(newsub, strpos + strlen(first), (MAX_INPUT_LENGTH - strlen(newsub) - 1));
  
    /* terminate the string in case of an overflow from strncat */
    newsub[MAX_INPUT_LENGTH - 1] = '\0';
    strcpy(subst, newsub);
  
    return (0);
  }
  
  
  
  void close_socket(struct descriptor_data *d)
  {
    char buf[128];
    struct descriptor_data *temp;
    long target_idnum = -1;
  
    REMOVE_FROM_LIST(d, descriptor_list, next);
    CLOSE_SOCKET(d->descriptor);
    flush_queues(d);
  
    /* 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;
    }
  
    /*. Kill any OLC stuff .*/
    switch(STATE(d))
    { case CON_OEDIT:
      case CON_REDIT:
      case CON_ZEDIT:
      case CON_MEDIT:
      case CON_SEDIT:
      case CON_TRIGEDIT:
        cleanup_olc(d, CLEANUP_ALL);
      default:
        break;
    }
  
    if (d->character) {
      /*
       * Plug memory leak, from Eric Green.
       */
      if (!IS_NPC(d->character) && PLR_FLAGGED(d->character, PLR_MAILING) &&
        d->str)
      {
        if (*(d->str))
          free(*(d->str));
        free(d->str);
      }
  
     target_idnum = GET_IDNUM(d->character);
      if (d->character->player.name)
      {
        CHOOSE_NAME(d->character, chname);
        if (STATE(d) == CON_PLAYING || STATE(d) == CON_DISCONNECT) {
          act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
          if (!IS_NPC(d->character)) {
            int saveRoom = NOWHERE;
            saveRoom = real_room(GET_LOADROOM(d->character));
            save_char(d->character, saveRoom);
            sprintf(buf, "Closing link to: %s.", chname);
            mudlog(buf, NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE);
          }
          d->character->desc = NULL;
        } else {
          die_follower(d->character);
          sprintf(buf, "Losing player: %s.", (chname == NULL) ? chname : "<null>");
          mudlog(buf, NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(d->character)), TRUE);
          free_char(d->character);
        }
        FREE_NAME(chname);
      }
    }
  
  
  #ifdef GMB_DEBUG
    else
      mudlog("Losing descriptor without char.", CMP, LVL_IMMORT, TRUE);
  #endif
  
  
    /* JE 2/22/95 -- part of my unending quest to make switch stable */
    if (d->original && d->original->desc)
      d->original->desc = NULL;
  
    /* Clear the command history. */
    if (d->history) {
      int cnt;
      for (cnt = 0; cnt < HISTORY_SIZE; cnt++)
        if (d->history[cnt])
          free(d->history[cnt]);
      free(d->history);
    }
  
    if (d->showstr_head)
    {
      free(d->showstr_head);
      d->showstr_head = NULL;
    }
  
    if (d->showstr_count)
    {
      free(d->showstr_vector);
      d->showstr_vector = NULL;
    }
  
    free(d);
    d = NULL;
  }
  
  
  
  void check_idle_passwords(void)
  {
    struct descriptor_data *d, *next_d;
  
    for (d = descriptor_list; d; d = next_d) {
      next_d = d->next;
      if (STATE(d) != CON_PASSWORD && STATE(d) != CON_GET_NAME)
        continue;
      if (!d->idle_tics) {
        d->idle_tics++;
        continue;
      } else {
        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.)
   */
  
  #if defined(CIRCLE_WINDOWS)
  
  void nonblock(socket_t s)
  {
    unsigned long val = 1;
    ioctlsocket(s, FIONBIO, &val);
  }
  
  #elif defined(CIRCLE_AMIGA)
  
  void nonblock(socket_t s)
  {
    long val = 1;
    IoctlSocket(s, FIONBIO, &val);
  }
  
  #elif defined(CIRCLE_UNIX) || defined(CIRCLE_OS2)
  
  #ifndef O_NONBLOCK
  #define O_NONBLOCK O_NDELAY
  #endif
  
  void nonblock(socket_t s)
  {
    int flags;
  
    flags = fcntl(s, F_GETFL, 0);
    flags |= O_NONBLOCK;
    if (fcntl(s, F_SETFL, flags) < 0) {
      perror("SYSERR: Fatal error executing nonblock (comm.c)");
      exit(1);
    }
  }
  
  #endif  /* CIRCLE_UNIX || CIRCLE_OS2 */
  
  
  /* ******************************************************************
  *  signal-handling functions (formerly signals.c).  UNIX only.      *
  ****************************************************************** */
  
  
  #ifdef CIRCLE_UNIX
  
  RETSIGTYPE checkpointing(int sig)
  {
    if (!tics) {
      log("SYSERR: CHECKPOINT shutdown: tics not updated");
      abort();
    } else
      tics = 0;
  }
  
  
  RETSIGTYPE reread_wizlists(int sig)
  {
    mudlog("Signal received - rereading wizlists.", CMP, LVL_IMMORT, TRUE);
    reboot_wizlists();
  }
  
  
  RETSIGTYPE unrestrict_game(int sig)
  {
    mudlog("Received SIGUSR2 - completely unrestricting game (emergent)",
  	 BRF, LVL_IMMORT, TRUE);
    ban_list = NULL;
    circle_restrict = 0;
    num_invalid = 0;
  }
  
  
  RETSIGTYPE intsig(int sig)
  {
    log("SYSERR: Received SIGINT, shutting down...");
    exit(0);			/* perhaps something more elegant should
  				 * substituted */
  }
  
  RETSIGTYPE termsig(int sig)
  {
    log("SYSERR: Received SIGTERM, shutting down...");
    exit(0);			/* perhaps something more elegant should
  				 * substituted */
  }
  
  RETSIGTYPE hupsig(int sig)
  {
    log("SYSERR: Received SIGHUP, shutting down...");
    exit(0);			/* perhaps something more elegant should
  				 * substituted */
  }
  
  RETSIGTYPE reap(int sig)
  {
    while (waitpid(-1, NULL, WNOHANG) > 0);
  
    my_signal(SIGCHLD, reap);
  }
  
  /*
   * 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.
   */
  
  #ifndef POSIX
  #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				/* POSIX */
  
  
  void signal_setup(void)
  {
    struct itimerval itime;
    struct timeval interval;
  
    /* user signal 1: reread wizlists.  Used by autowiz system. */
    my_signal(SIGUSR1, reread_wizlists);
  
    /*
     * user signal 2: unrestrict game.  Used for emergencies if you lock
     * yourself out of the MUD somehow.  (Duh...)
     */
    my_signal(SIGUSR2, unrestrict_game);
  
    /*
     * set up the deadlock-protection so that the MUD aborts itself if it gets
     * caught in an infinite loop for more than 3 minutes.
     */
    interval.tv_sec = 180;
    interval.tv_usec = 0;
    itime.it_interval = interval;
    itime.it_value = interval;
    setitimer(ITIMER_VIRTUAL, &itime, NULL);
    my_signal(SIGVTALRM, checkpointing);
  
    /* just to be on the safe side: */
    /* my_signal(SIGHUP, hupsig); */
    my_signal(SIGHUP, SIG_IGN);
    my_signal(SIGINT, intsig);
    my_signal(SIGTERM, termsig);
    my_signal(SIGPIPE, SIG_IGN);
    my_signal(SIGALRM, SIG_IGN);
  }
  
  #endif				/* CIRCLE_UNIX */
  
  
  /* ****************************************************************
  *       Public routines for system-to-player-communication        *
  **************************************************************** */
  
  void send_to_char(const char *messg, struct char_data *ch)
  {
    if (ch->desc && messg)
      SEND_TO_Q(messg, ch->desc);
  }
  
  
  void send_to_all(const char *messg)
  {
    struct descriptor_data *i;
  
    if (messg == NULL)
      return;
  
    for (i = descriptor_list; i; i = i->next)
      if (STATE(i) == CON_PLAYING)
        SEND_TO_Q(messg, i);
  }
  
  
  void send_to_outdoor(const char *messg)
  {
    struct descriptor_data *i;
  
    if (!messg || !*messg)
      return;
  
    for (i = descriptor_list; i; i = i->next)
    {
       if (STATE(i) != CON_PLAYING || i->character == NULL)
         continue;
       if (!AWAKE(i->character) || !OUTSIDE(i->character))
         continue;
       SEND_TO_Q(messg, i);
    }
  }
  
  
  
  void send_to_room(const char *messg, room_rnum room)
  {
    struct char_data *i;
  
    if (messg == NULL)
      return;
  
    for (i = world[room].people; i; i = i->next_in_room)
      if (i->desc)
        SEND_TO_Q(messg, i->desc);
  }
  
  static char *ACTNULL = "<NULL>";
  
  #if 1
  #define CHECK_NULL(pointer, expression) \
    if ((pointer) == NULL) strcpy(tbuf, ACTNULL); else strcpy(tbuf, expression);
  #else
  #define CHECK_NULL(pointer, expression) \
    if ((pointer) == NULL) i = ACTNULL; else i = (expression);
  #endif
  
  
  /* higher-level communication: the act() function */
  void perform_act(const char *orig, struct char_data *ch, struct obj_data *obj,
  		 const void *vict_obj, const struct char_data *to)
  {
    register char *i = NULL, *buf;
    static char lbuf[MAX_STRING_LENGTH];
    static char tbuf[MAX_STRING_LENGTH];
    struct char_data *dg_victim = NULL;
    struct obj_data *dg_target = NULL; 
    char *dg_arg = NULL;
  
    buf = lbuf;
  
    for (;;) {
      memset(tbuf, (char) NULL, MAX_STRING_LENGTH);
      if (*orig == '$') {
        switch (*(++orig)) {
        case 'n':
          GET_PERS(ch, to, chname);
          strcpy(tbuf, chname);
          FREE_NAME(chname);
  	/* i = PERS(ch, to); */
  	break;
        case 'N':
           GET_PERS((struct char_data*) vict_obj, to, chname);
  
  	CHECK_NULL(vict_obj, chname);
          FREE_NAME(chname);
          dg_victim = (struct char_data *) vict_obj;
  	break;
        case 'k':
          GET_PERS_II(ch, to, chname);
          strcpy(tbuf, chname);
          FREE_NAME(chname);
          /* i = PERS(ch, to); */
          break;
        case 'K':
           GET_PERS_II((struct char_data*) vict_obj, to, chname);
  
          CHECK_NULL(vict_obj, chname);
          FREE_NAME(chname);
          dg_victim = (struct char_data *) vict_obj;
          break;
        case 'm':
           strcpy(tbuf, HMHR(ch));
  	/* i = HMHR(ch); */
  	break;
        case 'M':
          if (PRF_FLAGGED((struct char_data *) vict_obj, PRF_NOTSELF)) {
          CHECK_NULL(vict_obj, HMHRD((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
          }
         else {
         CHECK_NULL(vict_obj, HMHR((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
          }
  	break;
        case 's':
           strcpy(tbuf, HSHR(ch));
  
  	/* i = HSHR(ch); */
  	break;
        case 'S':
            if (PRF_FLAGGED((struct char_data *) vict_obj, PRF_NOTSELF)) {
            CHECK_NULL(vict_obj, HSHRD((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
            }
           else {
           CHECK_NULL(vict_obj, HSHR((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
            }
  	break;
        case 'e':
            strcpy(tbuf, HSSH(ch));
  	/* i = HSSH(ch); */
  	break;
        case 'E':
          if (PRF_FLAGGED((struct char_data *) vict_obj, PRF_NOTSELF)) {
          CHECK_NULL(vict_obj, HSSHD((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
           }
          else {
           CHECK_NULL(vict_obj, HSSH((struct char_data *) vict_obj));
          dg_victim = (struct char_data *) vict_obj;
           }
  	break;
        case 'o':
  	CHECK_NULL(obj, OBJN(obj, to));
  	break;
        case 'O':
  	CHECK_NULL(vict_obj, OBJN((struct obj_data *) vict_obj, to));
          dg_target = (struct obj_data *) vict_obj;
  	break;
        case 'p':
           if (GET_OBJ_CREATION(obj) == 1 && GET_OBJ_NATURE(obj) == 1 && GET_CLASS(to) == CLASS_MAGE) {
              CHECK_NULL(obj, OBJT(obj, to));
            }
           else if (GET_OBJ_CREATION(obj) == 1 && GET_OBJ_NATURE(obj) == 2 && GET_CLASS(to) == CLASS_CLERIC) {
              CHECK_NULL(obj, OBJT(obj, to));
            }
  
         else {
             CHECK_NULL(obj, OBJS(obj, to));
           }
  	break;
        case 'P':
        
   if (GET_OBJ_CREATION(obj) ==1 && GET_OBJ_NATURE(obj) == 1 && GET_CLASS((struct char_data *) vict_obj) == CLASS_MAGE) {
          CHECK_NULL(vict_obj, OBJT((struct obj_data *) vict_obj, to));
          dg_target = (struct obj_data *) vict_obj;
           }
  
  else if (GET_OBJ_CREATION(obj) ==1 && GET_OBJ_NATURE(obj) == 2 && GET_CLASS((struct char_data *) vict_obj) == CLASS_CLERIC) {
          CHECK_NULL(vict_obj, OBJT((struct obj_data *) vict_obj, to));
          dg_target = (struct obj_data *) vict_obj;
           }
          else {
           CHECK_NULL(vict_obj, OBJS((struct obj_data *) vict_obj, to));
          dg_target = (struct obj_data *) vict_obj;
                    }
  
  	break;
        case 'a':
  	CHECK_NULL(obj, SANA(obj));
  	break;
        case 'A':
  	CHECK_NULL(vict_obj, SANA((struct obj_data *) vict_obj));
          dg_target = (struct obj_data *) vict_obj;
  	break;
        case 'T':
  	CHECK_NULL(vict_obj, (char *) vict_obj);
          dg_arg = (char *) vict_obj;
  	break;
        case 'F':
  	CHECK_NULL(vict_obj, fname((char *) vict_obj));
  	break;
        case '$':
          strcpy(tbuf, "$");
  	/* i = "$"; */
  	break;
        default:
  	log("SYSERR: Illegal $-code to act():");
  	strcpy(buf1, "SYSERR: ");
  	strcat(buf1, orig);
  	log(buf1);
  	break;
        }
  
        i = tbuf;
  
        while ((*buf = *(i++))) {
  	buf++;
        }
  
        orig++;
  
      } else if (!(*(buf++) = *(orig++))) {
        break;
      }
    }
  
    *(--buf) = '\r';
    *(++buf) = '\n';
    *(++buf) = '\0';
  
    if (to->desc) {
      SEND_TO_Q(CAP(lbuf), to->desc);
    }
  
    if ((IS_NPC(to) && dg_act_check) && (to != ch)) {
      act_mtrigger(to, lbuf, ch, dg_victim, obj, dg_target, dg_arg);
    }
  }
  
  
  /* moved this to utils.h --- mah */
  #ifndef SENDOK
  #define SENDOK(ch) ((ch)->desc && (AWAKE(ch) || sleep) && \
  		    !PLR_FLAGGED((ch), PLR_WRITING))
  #endif
  
  void act(const char *str, int hide_invisible, struct char_data *ch,
  	 struct obj_data *obj, const void *vict_obj, int type)
  {
    struct char_data *to = NULL;
    static int sleep, deaf;
  
    if (!str || !*str)
      return;
  
    if (!(dg_act_check = !(type & DG_NO_TRIG)))
      type &= ~DG_NO_TRIG;
  
    /*
     * 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.
     * TO_SLEEP only works because its value "happens to be" a single bit;
     * do not change it to something else.  In short, it is a hack.
     *
     * So is TO_NOTDEAF.  Soli, 9/15/99
     */
  
    /* check if TO_SLEEP is there, and remove it if it is. */
    if ((sleep = (type & TO_SLEEP)))
      type &= ~TO_SLEEP;
    if ((deaf = (type & TO_NOTDEAF)))
      type &= ~TO_NOTDEAF;
  
    if (type == TO_CHAR) {
      if (ch && SENDOK(ch))
        perform_act(str, ch, obj, vict_obj, ch);
      return;
    }
  
    if (type == TO_VICT) {
      if ( (to = (struct char_data *) vict_obj) &&
  	 SENDOK(to) &&
  	 !(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 */
  
    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 {
      log("SYSERR: no valid target to act()!");
      return;
    }
  
    for (; to; to = to->next_in_room)
      if (SENDOK(to) && !(hide_invisible && ch && !CAN_SEE(to, ch)) &&
  	(to != ch) && (type == TO_ROOM || (to != vict_obj)))
        perform_act(str, ch, obj, vict_obj, to);
  }
  
  /*
   * This function is called every 30 seconds from heartbeat().  It checks
   * the four global buffers in CircleMUD to ensure that no one has written
   * past their bounds.  If our check digit is not there (and the position
   * doesn't have a NUL which may result from snprintf) then we gripe that
   * someone has overwritten our buffer.  This could cause a false positive
   * if someone uses the buffer as a non-terminated character array but that
   * is not likely. -gg
   */
  void sanity_check(void)
  {
    int ok = TRUE;
  
    /*
     * If any line is false, 'ok' will become false also.
     */
    ok &= (test_magic(buf)  == MAGIC_NUMBER || test_magic(buf)  == '\0');
    ok &= (test_magic(buf1) == MAGIC_NUMBER || test_magic(buf1) == '\0');
    ok &= (test_magic(buf2) == MAGIC_NUMBER || test_magic(buf2) == '\0');
    ok &= (test_magic(arg)  == MAGIC_NUMBER || test_magic(arg)  == '\0');
  
    /*
     * This isn't exactly the safest thing to do (referencing known bad memory)
     * but we're doomed to crash eventually, might as well try to get something
     * useful before we go down. -gg
     * However, lets fix the problem so we don't spam the logs. -gg 11/24/98
     */
    if (!ok) {
      log("SYSERR: *** Buffer overflow! ***\n"
          "buf: %s\nbuf1: %s\nbuf2: %s\narg: %s", buf, buf1, buf2, arg);
  
      plant_magic(buf);
      plant_magic(buf1);
      plant_magic(buf2);
      plant_magic(arg);
    }
  
  #if 0
    log("Statistics: buf=%d buf1=%d buf2=%d arg=%d",
          strlen(buf), strlen(buf1), strlen(buf2), strlen(arg));
  #endif
  }
  
  /* Prefer the file over the descriptor. */
  void setup_log(const char *filename, int fd)
  {
    FILE *s_fp;
  
  #if defined(__MWERKS__) || defined(__GNUC__)
    s_fp = stderr;
  #else
    if ((s_fp = fdopen(STDERR_FILENO, "w")) == NULL) {
      puts("SYSERR: Error opening stderr, trying stdout.");
  
      if ((s_fp = fdopen(STDOUT_FILENO, "w")) == NULL) {
        puts("SYSERR: Error opening stdout, trying a file.");
  
        /* If we don't have a file, try a default. */
        if (filename == NULL || *filename == '\0')
          filename = "log/syslog";
      }
    }
  #endif
  
    if (filename == NULL || *filename == '\0') {
      /* No filename, set us up with the descriptor we just opened. */
      logfile = s_fp;
      puts("Using file descriptor for logging.");
      return;
    }
  
    /* We honor the default filename first. */
    if (open_logfile(filename, s_fp))
      return;
  
    /* Well, that failed but we want it logged to a file so try a default. */
    if (open_logfile("log/syslog", s_fp))
      return;
  
    /* Ok, one last shot at a file. */
    if (open_logfile("syslog", s_fp))
      return;
  
    /* Erp, that didn't work either, just die. */
    puts("SYSERR: Couldn't open anything to log to, giving up.");
    exit(1);
  }
  
  
  int open_logfile(const char *filename, FILE *stderr_fp)
  {
    if (stderr_fp)        /* freopen() the descriptor. */
      logfile = freopen(filename, "w", stderr_fp);
    else
      logfile = fopen(filename, "w");
  
    if (logfile) {
      printf("Using log file '%s'%s.\n",
                  filename, stderr_fp ? " with redirection" : "");
      return (TRUE);
    }
  
    printf("SYSERR: Error opening file '%s': %s\n", filename, strerror(errno));
    return (FALSE);
  }
  
  int enter_player_game(struct descriptor_data *d);
  
  /* Reload players after a copyover */
  void copyover_recover()
  {
  	struct descriptor_data *d;
  	FILE *fp;
  	char host[1024];
      struct char_file_u tmp_store;
  	int desc, player_i;
      bool fOld;
      char name[MAX_INPUT_LENGTH];
  	
      log ("Copyover recovery initiated");
  
      fp = fopen (COPYOVER_FILE, "r");
      /* there are some descriptors open which will hang forever then ? */
       if (!fp) {
         perror ("copyover_recover:fopen");
         log ("Copyover file not found. Exitting.\n\r");
         exit (1);
       }
  
       /* In case something crashes - doesn't prevent reading	*/
       unlink (COPYOVER_FILE);	
         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 ((player_i = load_char(name, &tmp_store)) >= 0) {
              store_to_char(&tmp_store, d->character);
              GET_PFILEPOS(d->character) = player_i;
              if (!PLR_FLAGGED(d->character, PLR_DELETED))
                  REMOVE_BIT(PLR_FLAGS(d->character),PLR_WRITING | PLR_MAILING);
              else
               fOld = FALSE;
          }
          else
          fOld = FALSE;
  
          /* Player file not found?! */
  	if (!fOld) {
  	write_to_descriptor (desc, "\n\rSomehow, your character was lost in the copyover. Sorry.\n\r");
  	close_socket (d);			
  	} else 	{
              write_to_descriptor (desc, "\n\rCopyover recovery complete.\n\r");
              enter_player_game(d);
              d->connected = CON_PLAYING;
              look_at_room(d->character, 0);
  	 }
         }
    fclose (fp);
  }
  
  /* Initialize a descriptor */
  void init_descriptor (struct descriptor_data *newd, int desc)
  {
      static int last_desc = 0;	/* last descriptor number */
      
    newd->descriptor = desc;
    newd->idle_tics = 0;
    newd->output = newd->small_outbuf;
    newd->bufspace = SMALL_BUFSIZE - 1;
    newd->login_time = time(0);
    *newd->output = '\0';
    newd->bufptr = 0;
    newd->has_prompt = 1;  /* prompt is part of greetings */
    STATE(newd) = CON_GET_NAME;
    CREATE(newd->history, char *, HISTORY_SIZE);
    if (++last_desc == 1000)
      last_desc = 1;
    newd->desc_num = last_desc;
  }
  
  
  
  /*
   * This may not be pretty but it keeps game_loop() neater than if it was inline.
   */
  #if defined(CIRCLE_WINDOWS)
  
  void circle_sleep(struct timeval *timeout)
  {
    Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
  }
  
  #else
  
  void circle_sleep(struct timeval *timeout)
  {
    if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, timeout) < 0) {
      if (errno != EINTR) {
        perror("SYSERR: Select sleep");
        exit(1);
      }
    }
  }
  
  #endif /* CIRCLE_WINDOWS */
  