pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
/* bsd.c */

#include "copyrite.h"
#include "config.h"

#include <stdio.h>
#ifdef I_STDARG
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#ifdef I_MEMORY
#include <memory.h>
#endif
#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#ifdef WIN32
#define FD_SETSIZE 256
#include <winsock.h>
#include <io.h>
#undef OPAQUE			/* Clashes with flags.h */
#define EINTR WSAEINTR
#define EMFILE WSAEMFILE
#define EWOULDBLOCK WSAEWOULDBLOCK
#define MAXHOSTNAMELEN 32
void shutdown_checkpoint _((void));
#ifdef INFO_SLAVE
#undef INFO_SLAVE
#endif
#else				/* WIN32 */
#include <sys/file.h>
#ifdef I_SYS_TIME
#include <sys/time.h>
#endif
#include <sys/ioctl.h>
#ifdef I_ERRNO
#include <errno.h>
#else
#ifdef I_SYS_ERRNO
#include <sys/errno.h>
#endif
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/param.h>
#endif				/* WIN32 */
#ifdef I_TIME
#include <time.h>
#endif
#ifdef I_SYS_WAIT
#include <sys/wait.h>
#endif
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef I_SYS_SELECT
#include <sys/select.h>
#endif
#ifdef I_UNISTD
#include <unistd.h>
#endif
#ifdef HAS_GETRLIMIT
#include <sys/resource.h>
#endif
#ifdef I_LIMITS
#include <limits.h>
#endif
#ifdef I_ARPA_INET
#include <arpa/inet.h>
#endif
#ifdef I_FLOATINGPOINT
#include <floatingpoint.h>
#endif

#include "conf.h"
#include "mushdb.h"
#include "intrface.h"
#include "externs.h"
#include "globals.h"
#include "help.h"
#include "match.h"
#include "ansi.h"
#include "pueblo.h"
#include "parse.h"
#include "access.h"
#include "version.h"
#include "ident.h"

#ifdef MEM_CHECK
#include "memcheck.h"
#endif

#include "mymalloc.h"

#ifdef CHAT_SYSTEM
#include "extchat.h"
extern CHAN *channels;
struct na_cpass {
  CHANUSER *u;
  int checkquiet;
};

#endif
#include "confmagic.h"

/* BSD 4.2 and maybe some others need these defined */
#ifndef FD_ZERO
#define fd_set int
#define FD_ZERO(p)       (*p = 0)
#define FD_SET(n,p)      (*p |= (1<<(n)))
#define FD_CLR(n,p)      (*p &= ~(1<<(n)))
#define FD_ISSET(n,p)    (*p & (1<<(n)))
#endif				/* defines for BSD 4.2 */

/* Win32 uses closesocket() to close a socket, and so will we */
#ifndef WIN32
#define closesocket(s)  close(s)
#endif

/* What htons expects */
#ifdef CAN_NEWSTYLE
#define Port_t unsigned short
#else
#define Port_t int
#endif

#ifdef HAS_GETRUSAGE
extern void rusage_stats _((void));
#endif
extern int que_next _((void));	/* from cque.c */
extern void dispatch _((void));	/* from timer.c */
extern dbref email_register_player _((const char *name, const char *email, const char *host));	/* from player.c */
static int how_many_fds _((void));
static void fillstate _((int state[], char **f));

extern time_t start_time;
extern dbref orator;
#ifndef WIN32
extern int errno;
#endif
extern int reserved;
static int extrafd;
int shutdown_flag = 0;
extern dbref db_top;

static int login_number = 0;
static int under_limit = 1;

char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN], cf_downmotd_msg[BUFFER_LEN],
 cf_fullmotd_msg[BUFFER_LEN];
static char poll_msg[DOING_LEN];
char confname[BUFFER_LEN];
char errlog[BUFFER_LEN];

struct text_block {
  int nchars;
  struct text_block *nxt;
  char *start;
  char *buf;
};

struct text_queue {
  struct text_block *head;
  struct text_block **tail;
};

struct descriptor_data {
  int descriptor;
  int connected;
  char addr[101];
  dbref player;
  char *output_prefix;
  char *output_suffix;
  int output_size;
  struct text_queue output;
  struct text_queue input;
  char *raw_input;
  char *raw_input_at;
  long connected_at;
  long last_time;
  int quota;
  int cmds;
  int hide;
  char doing[DOING_LEN];
  struct descriptor_data *next;
  struct descriptor_data *prev;
#ifdef USE_MAILER
  struct mail *mailp;
#endif
  int pueblo;
};

#define DESC_ITER_CONN(d) \
        for(d = descriptor_list;(d);d=(d)->next) \
	  if((d)->connected)

#define Hidden(d)        ((d->hide == 1) && Can_Hide(d->player))

/* log file pointers */
FILE *connlog_fp;
FILE *checklog_fp;
FILE *wizlog_fp;
FILE *tracelog_fp;
FILE *cmdlog_fp;

static const char *connect_fail = "Either that player does not exist, or has a different password.\r\n";
static const char *create_fail = "Either there is already a player with that name, or that name is illegal.\r\n";
static const char *register_fail = "Unable to register that player with that email address.\r\n";
static const char *register_success = "Registration successful! You will receive your password by email.\r\n";
#ifdef OLD_ANSI
static const char *flushed_message = "\r\n<Output Flushed>\033[0m\r\n";
#else
static const char *flushed_message = "\r\n<Output Flushed>\x1B[0m\r\n";
#endif
static const char *shutdown_message = "Going down - Bye\r\n";
static const char *asterisk_line =
"**********************************************************************";
#define REBOOTFILE		"reboot.db"

struct descriptor_data *descriptor_list = 0;

static int sock;
static int ndescriptors = 0;
int restarting = 0;
static int maxd;
char ccom[BUFFER_LEN];
dbref cplr;
DESC *cdesc = 0;

#ifdef INFO_SLAVE
static fd_set info_pending;
static int info_slave;
static Pid_t info_slave_pid;
static int info_slave_state = 0;
static int info_query_spill, info_reap_spill;
static time_t info_queue_time = 0;
#endif

#ifdef HAS_GETRLIMIT
static void init_rlimit _((void));
#endif
#ifndef BOOLEXP_DEBUGGING
#ifdef WIN32
void mainthread _((int argc, char **argv));
#else
int main _((int argc, char **argv));
#endif
#endif
void set_signals _((void));
void raw_notify _((dbref player, const char *msg));
struct timeval timeval_sub _((struct timeval now, struct timeval then));
long int msec_diff _((struct timeval now, struct timeval then));
struct timeval msec_add _((struct timeval t, int x));
struct timeval update_quotas _((struct timeval last, struct timeval current));
void shovechars _((Port_t port));
struct descriptor_data *new_connection _((int oldsock));
void clearstrings _((struct descriptor_data * d));
void fcache_dump _((DESC *d, FBLOCK *fp[]));
int fcache_read _((FBLOCK **cp, char *filename));
void fcache_load _((dbref player));
void fcache_init _((void));
void logout_sock _((struct descriptor_data * d));
void shutdownsock _((struct descriptor_data * d));
struct descriptor_data *initializesock _((int s, char *addr));
int make_socket _((Port_t port));
struct text_block *make_text_block _((const char *s, int n));
void free_text_block _((struct text_block * t));
void add_to_queue _((struct text_queue * q, const char *b, int n));
int flush_queue _((struct text_queue * q, int n));
int queue_write _((struct descriptor_data * d, const char *b, int n));
int queue_newwrite _((struct descriptor_data * d, const char *b, int n));
int queue_string _((struct descriptor_data * d, const char *s));
int process_output _((struct descriptor_data * d));
void make_nonblocking _((int s));
void freeqs _((struct descriptor_data * d));
void welcome_user _((struct descriptor_data * d));
void do_new_spitfile _((dbref player, char *arg1, char *index_file, char *text_file, int restricted));
static void dump_info _((DESC *call_by));
void save_command _((struct descriptor_data * d, const char *command));
int process_input _((struct descriptor_data * d));
void set_userstring _((char **userstring, const char *command));
void process_commands _((void));
int do_command _((struct descriptor_data * d, char *command));
static int dump_messages _((DESC *d, dbref player, int new));
int check_connect _((struct descriptor_data * d, const char *msg));
void parse_connect _((const char *msg, char *command, char *user, char *pass));
void close_sockets _((void));
void emergency_shutdown _((void));
dbref find_player_by_desc _((int port));
Signal_t bailout _((int sig));
void dump_users _((struct descriptor_data * call_by, char *match, int doing));
const char *time_format_1 _((long int dt));
const char *time_format_2 _((long int dt));
void announce_connect _((dbref player));
void announce_disconnect _((dbref player));
void do_motd _((dbref player, int key, const char *message));
void do_doing _((dbref player, const char *message));
void do_poll _((dbref player, const char *message));
const char *hostname_convert _((struct in_addr nums));
dbref short_page _((const char *match));
void hide_player _((dbref player, int hide));
void inactivity_check _((void));
#ifdef CHAT_SYSTEM
void do_channel_who _((dbref player, CHAN *chan));
#endif
int hidden _((dbref player));
#ifdef USE_MAILER
struct mail *desc_mail _((dbref player));
void desc_mail_set _((dbref player, struct mail * mp));
#endif
void rwho_update _((void));
#ifdef INFO_SLAVE
static void make_info_slave _((void));
static void promote_info_slave _((void));
static void query_info_slave _((int fd));
static void reap_info_slave _((void));
void kill_info_slave _((void));
#endif
extern void local_shutdown _((void));
void reopen_logs _((void));
void dump_reboot_db _((void));
void load_reboot_db _((void));

#ifdef HAS_GETRLIMIT
static void
init_rlimit()
{
  /* Unlimit file descriptors. */
  /* Ultrix 4.4 and others may have getrlimit but may not be able to
   * change number of file descriptors 
   */
#ifdef RLIMIT_NOFILE
  struct rlimit *rlp;

  rlp = (struct rlimit *) malloc(sizeof(rlp));
  if (getrlimit(RLIMIT_NOFILE, rlp)) {
    perror("init_rlimit: getrlimit()");
    free(rlp);
    return;
  }
  rlp->rlim_cur = rlp->rlim_max;
  if (setrlimit(RLIMIT_NOFILE, rlp))
    perror("init_rlimit: setrlimit()");
  free(rlp);
#endif
  return;
}
#endif				/* HAS_GETRLIMIT */

#ifndef BOOLEXP_DEBUGGING
#ifdef WIN32
/* Under WIN32, MUSH is a "service", so we just start a thread here.
 * The real "main" is in win32/services.c
 */
void
mainthread(argc, argv)
    int argc;
    char **argv;
#else
int
main(argc, argv)
    int argc;
    char **argv;
#endif				/* WIN32 */
{
#ifdef AUTORESTART
  FILE *id;
#endif
  FILE *newerr;

  /* read the configuration file */
  if (argc < 3) {
    fprintf(stderr, "ERROR: Usage: %s config_file error_log_file\n", argv[0]);
    exit(2);
  }
#ifdef WIN32
  {
    unsigned short wVersionRequested = MAKEWORD(1, 1);
    WSADATA wsadata;
    int err;

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

#ifdef HAS_GETRLIMIT
  init_rlimit();		/* unlimit file descriptors */
#endif

  /* These are FreeBSDisms to fix floating point exceptions */
#ifdef HAS_FPSETROUND
  fpsetround(FP_RN);
#endif
#ifdef HAS_FPSETMASK
  fpsetmask(0L);
#endif

  /* open the log files */
  strncpy(errlog, argv[2], BUFFER_LEN - 1);
  errlog[BUFFER_LEN - 1] = '\0';
  newerr = fopen(errlog, "a");
  if (!newerr) {
    fprintf(stderr, "Unable to open %s. Error output to stderr.\n", errlog);
  } else {
    fprintf(stderr, "Redirecting output to: %s\n", errlog);
    if (!freopen(errlog, "a", stderr)) {
      printf("Ack!  Failed reopening stderr!");
      exit(1);
    }
    setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
    fclose(newerr);
  }
#ifndef SINGLE_LOGFILE
  start_log(&connlog_fp, CONNLOG);
  start_log(&checklog_fp, CHECKLOG);
  start_log(&wizlog_fp, WIZLOG);
  start_log(&tracelog_fp, TRACELOG);
  start_log(&cmdlog_fp, CMDLOG);
#else
  connlog_fp = checklog_fp = wizlog_fp = tracelog_fp = cmdlog_fp = stderr;
#endif				/* SINGLE_LOGFILE */

/*  this writes a file used by the restart script to check for active mush */
#ifdef AUTORESTART
  id = fopen("runid", "w");
  fprintf(id, "%d", getpid());
  fclose(id);
#endif

#ifdef INFO_SLAVE
  make_info_slave();
#endif

  /* save a file descriptor */
#ifndef WIN32
  reserved = open("/dev/null", O_RDWR);
  extrafd = open("/dev/null", O_RDWR);
#endif

  /* decide if we're in @shutdown/reboot */
  restarting = 0;
  newerr = fopen(REBOOTFILE, "r");
  if (newerr) {
    restarting = 1;
    fclose(newerr);
  }
  init_process_expression();

  strncpy(confname, argv[1], BUFFER_LEN - 1);
  confname[BUFFER_LEN - 1] = '\0';
  if (init_game(confname) < 0) {
    fprintf(stderr, "ERROR: Couldn't load databases! Exiting.\n");
    exit(2);
  }
  set_signals();
  if (USE_RWHO)
    rwhocli_setup(RWHOSERV, RWHOPASS, MUDNAME, SHORTVN);

  /* go do it */
#ifdef CSRI
#ifdef CSRI_DEBUG
  mal_verify(1);
#endif
#ifdef CSRI_TRACE
  mal_leaktrace(1);
#endif
#endif
  load_reboot_db();
  shovechars((Port_t) TINYPORT);
#ifdef CSRI
#ifdef CSRI_DEBUG
  mal_verify(1);
#endif
#endif

  /* someone has told us to shut down */

#ifdef ALLOW_RPAGE
  rpage_shutdown();		/* do this first */
#endif				/* ALLOW_RPAGE */

#ifdef WIN32
  /* Keep service manager happy */
  shutdown_checkpoint();
#endif

  close_sockets();

#ifdef WIN32
  /* Keep service manager happy */
  shutdown_checkpoint();
#endif

  dump_database();

  local_shutdown();

#ifndef SINGLE_LOGFILE
  /* close up the log files */
  end_log(connlog_fp);
  end_log(checklog_fp);
  end_log(wizlog_fp);
  end_log(tracelog_fp);
  end_log(cmdlog_fp);
#endif				/* SINGLE_LOGFILE */

#ifdef CSRI
#ifdef CSRI_PROFILESIZES
  mal_statsdump(stderr);
#endif
#ifdef CSRI_TRACE
  mal_dumpleaktrace(stderr);
#endif
  fflush(stderr);
#endif

#ifdef WIN32
  /* Keep service manager happy */
  shutdown_checkpoint();
#endif

#ifdef HAS_GETRUSAGE
  rusage_stats();
#endif				/* HAS_RUSAGE */

  fprintf(stderr, "\nMUSH shutdown completed.\n");
  fflush(stderr);

  closesocket(sock);
#ifdef WIN32
  shutdown_checkpoint();
  WSACleanup();			/* clean up */
  kill_timer();			/* stop timer thread */
#else
  exit(0);
#endif
}
#endif				/* BOOLEXP_DEBUGGING */

/* Close and reopen the logfiles - called on SIGHUP */
void
reopen_logs()
{
  FILE *newerr;
#ifndef SINGLE_LOGFILE
  /* close up the log files */
  end_log(connlog_fp);
  end_log(checklog_fp);
  end_log(wizlog_fp);
  end_log(tracelog_fp);
  end_log(cmdlog_fp);
#endif				/* SINGLE_LOGFILE */
  newerr = fopen(errlog, "a");
  if (!newerr) {
    fprintf(stderr, "Unable to open %s. Error output continues to stderr.\n", errlog);
  } else {
    if (!freopen(errlog, "a", stderr)) {
      printf("Ack!  Failed reopening stderr!");
      exit(1);
    }
    setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
    fclose(newerr);
  }
#ifndef SINGLE_LOGFILE
  start_log(&connlog_fp, CONNLOG);
  start_log(&checklog_fp, CHECKLOG);
  start_log(&wizlog_fp, WIZLOG);
  start_log(&tracelog_fp, TRACELOG);
  start_log(&cmdlog_fp, CMDLOG);
#else
  connlog_fp = checklog_fp = wizlog_fp = tracelog_fp = cmdlog_fp = stderr;
#endif				/* SINGLE_LOGFILE */
}

void
set_signals()
{
#ifndef WIN32
  /* we don't care about SIGPIPE, we notice it in select() and write() */
  signal(SIGPIPE, SIG_IGN);
#endif

  /* standard termination signals */
  signal(SIGINT, (void *) bailout);
  signal(SIGTERM, (void *) bailout);
}

#define NA_RAW -1
#define NA_ASCII 0
#define NA_ANSI 1
#define NA_COLOR 2
#define NA_PUEBLO 3
#define NA_PASCII 4

#define TA_BGC 0
#define TA_FGC 1
#define TA_BOLD 2
#define TA_REV 3
#define TA_BLINK 4
#define TA_ULINE 5

static int na_depth = 0;

struct notify_strings {
  char message[BUFFER_LEN];
  int made;
};

static void
fillstate(state, f)
    int state[6];
    char **f;
{
  char *p;
  int i;
  int n;
  p = *f;
  p++;
  if (*p != '[') {
    while (*p && *p != 'm')
      p++;
  } else {
    p++;
    while (*p && *p != 'm') {
      if ((*p > '9') || (*p < '0')) {
	/* Nada */
      } else if (!(*(p + 1)) || (*(p + 1) == 'm') || (*(p + 1) == ';')) {
	/* ShortCode */ ;
	switch (*p) {
	case '0':
	  for (i = 0; i < 6; i++)
	    state[i] = 0;
	  break;
	case '1':
	  state[TA_BOLD] = 1;
	  break;
	case '7':
	  state[TA_REV] = 1;
	  break;
	case '5':
	  state[TA_BLINK] = 1;
	  break;
	case '4':
	  state[TA_ULINE] = 1;
	  break;
	}
      } else {
	n = (*p - '0') * 10;
	p++;
	n += (*p - '0');
	if ((n >= 30) && (n <= 37))
	  state[TA_FGC] = n - 29;
	else if ((n >= 40) && (n <= 47))
	  state[TA_BGC] = n - 39;
      }
      p++;
    }
  }
  if ((p != *f) && (*p != 'm'))
    p--;
  *f = p;
}

int html_cols[] =
{
  0xC0C0C0,			/* Default */
  0x000000,
  0xC00000,
  0x00C000,
  0xC0C000,
  0x0000C0,
  0xC000C0,
  0x00C0C0,
  0xC0C0C0
};

static char *
notify_makestring(message, messages, type)
    char *message;
    struct notify_strings messages[];
    int type;
{
  char *o;
  char *p;
  char *t;
  int state[6];
  int newstate[6];
  int n;
  int b, f;
  int i;
  int changed = 0;
  int color = 0;
  int fc, bc;
  char colbuff[128];
  if (messages[type].made)
    return messages[type].message;
  messages[type].made = 1;

  p = message;
  o = messages[type].message;

  /* Since well over 50% is this type, we do it quick */
  if (type == NA_ASCII) {
    while (*p) {
      switch (*p) {
      case TAG_START:
	while (*p && *p != TAG_END)
	  p++;
	break;
      case '\r':
	break;
      case ESC_CHAR:
	while (*p && *p != 'm')
	  p++;
	break;
      default:
	*o++ = *p;
      }
      p++;
    }
    *o = '\0';
    return messages[type].message;
  } else if (type == NA_PASCII) {
    /* PLAYER Ascii. Different output. \n is \r\n */
    while (*p) {
      switch (*p) {
      case TAG_START:
	while (*p && *p != TAG_END)
	  p++;
	break;
      case ESC_CHAR:
	while (*p && *p != 'm')
	  p++;
	break;
      case '\r':
	break;
      case '\n':
	*o++ = '\r';
	*o++ = '\n';
	break;
      default:
	*o++ = *p;
      }
      p++;
    }
    *o = '\0';
    return messages[type].message;
  }
  for (n = 0; n < 6; n++) {
    state[n] = 0;
    newstate[n] = 0;
  }

  t = o;
  switch (type) {
  case NA_PUEBLO:
    while (*p) {
      if (*p == ESC_CHAR) {
	fillstate(newstate, &p);
	changed = 1;
      } else {
	if (changed) {
	  changed = 0;
	  if ((state[TA_BGC] && (state[TA_BGC] != newstate[TA_BGC])) ||
	      (state[TA_FGC] && (state[TA_FGC] != newstate[TA_FGC])) ||
	      (state[TA_REV] != newstate[TA_REV])) {
	    safe_str("</FONT>", t, &o);
	  }
	  if (newstate[TA_BGC] || newstate[TA_FGC] || newstate[TA_REV]) {
	    fc = newstate[TA_FGC];
	    bc = newstate[TA_BGC];
	    if (newstate[TA_REV]) {
	      if (!fc)
		fc = 8;
	      if (!bc)
		bc = 1;
	      f = html_cols[bc];
	      b = html_cols[fc];
	    } else {
	      f = html_cols[fc];
	      b = html_cols[bc];
	    }
	    if (bc && fc) {
	      sprintf(colbuff, "<FONT FGCOLOR=#%x BGCOLOR=#%x>", f, b);
	    } else if (fc) {
	      sprintf(colbuff, "<FONT FGCOLOR=#%x>", f);
	    } else {
	      sprintf(colbuff, "<FONT BGCOLOR=#%x>", b);
	    }
	    safe_str(colbuff, t, &o);
	  }
	  if (state[TA_BOLD] && !newstate[TA_BOLD])
	    safe_str("</B>", t, &o);
	  else if (!state[TA_BOLD] && newstate[TA_BOLD])
	    safe_str("<B>", t, &o);
	  if (state[TA_ULINE] && !newstate[TA_ULINE])
	    safe_str("</U>", t, &o);
	  else if (!state[TA_ULINE] && newstate[TA_ULINE])
	    safe_str("<U>", t, &o);
	  for (n = 0; n < 6; n++)
	    state[n] = newstate[n];
	}
	switch (*p) {
	case TAG_START:
	  safe_chr('<', t, &o);
	  p++;
	  while ((*p) && (*p != TAG_END)) {
	    safe_chr(*p, t, &o);
	    p++;
	  }
	  safe_chr('>', t, &o);
	  break;
	case TAG_END:
	  /* Should never be seen alone */
	  break;
	case '<':
	  safe_str("&lt;", t, &o);
	  break;
	case '>':
	  safe_str("&gt;", t, &o);
	  break;
	case '"':
	  safe_str("&quot;", t, &o);
	  break;
	case '&':
	  safe_str("&amp;", t, &o);
	  break;
	case '\r':
	  break;
	case '\n':
	  safe_str("<BR>\n", t, &o);
	  break;
	default:
	  safe_chr(*p, t, &o);
	  break;
	}
      }
      p++;
    }
    if (state[TA_BGC] || state[TA_FGC] || state[TA_REV])
      safe_str("</FONT>", t, &o);
    if (state[TA_BOLD])
      safe_str("</B>", t, &o);
    if (state[TA_ULINE])
      safe_str("</U>", t, &o);
    break;
  case NA_COLOR:
    color = 1;
    /* FALLTHROUGH */
  case NA_ANSI:
    while (*p) {
      switch (*p) {
      case TAG_START:
	while (*p && *p != TAG_END)
	  p++;
	break;
      case '\r':
	break;
      case '\n':
	safe_str("\r\n", t, &o);
	break;
      case ESC_CHAR:
	fillstate(newstate, &p);
	changed = 1;
	break;
      default:
	if (changed) {
	  changed = 0;
	  if (
	       (state[TA_BOLD] && !newstate[TA_BOLD]) ||
	       (state[TA_REV] && !newstate[TA_REV]) ||
	       (state[TA_BLINK] && !newstate[TA_BLINK]) ||
	       (state[TA_ULINE] && !newstate[TA_ULINE]) ||
	       (color && state[TA_FGC] && !newstate[TA_FGC]) ||
	       (color && state[TA_BGC] && !newstate[TA_BGC])
	    ) {
	    for (n = 0; n < 6; n++)
	      state[n] = 0;
	    safe_str(ANSI_NORMAL, t, &o);
	  }
	  i = 0;
	  if (
	  (newstate[TA_BOLD] && (newstate[TA_BOLD] != state[TA_BOLD])) ||
	     (newstate[TA_REV] && (newstate[TA_REV] != state[TA_REV])) ||
	       (newstate[TA_BLINK] && (newstate[TA_BLINK] != state[TA_BLINK])) ||
	       (newstate[TA_ULINE] && (newstate[TA_ULINE] != state[TA_ULINE])) ||
	       (color && newstate[TA_FGC] && (newstate[TA_FGC] != state[TA_FGC])) ||
	       (color && newstate[TA_BGC] && (newstate[TA_BGC] != state[TA_BGC]))
	    ) {
	    safe_chr(ESC_CHAR, t, &o);
	    safe_chr('[', t, &o);
	    i = 0;
#define add_ansi_if(x,c) \
            if (newstate[(x)] && (newstate[(x)]!=state[(x)])) { \
              if (i) safe_chr(';',t,&o); \
              safe_str((c),t,&o); \
              i=1; \
            }
	    add_ansi_if(TA_BOLD, "1");
	    add_ansi_if(TA_REV, "7");
	    add_ansi_if(TA_BLINK, "5");
	    add_ansi_if(TA_ULINE, "4");
	    if (color) {
	      add_ansi_if(TA_FGC, unparse_number(newstate[TA_FGC] + 29));
	      add_ansi_if(TA_BGC, unparse_number(newstate[TA_BGC] + 39));
	    }
	    safe_chr('m', t, &o);
	  }
	  for (n = 0; n < 6; n++)
	    state[n] = newstate[n];
	}
	safe_chr(*p, t, &o);
      }
      p++;
    }
    if (state[TA_BOLD] || state[TA_REV] ||
	state[TA_BLINK] || state[TA_ULINE] ||
	(color && (state[TA_FGC] || state[TA_BGC])))
      safe_str(ANSI_NORMAL, t, &o);
    break;
  }

  *o = '\0';
  return messages[type].message;
}

dbref
na_one(current, data)
    dbref current;
    void *data;
{
  if (current == NOTHING)
    return (dbref) data;
  else
    return NOTHING;
}

dbref
na_next(current, data)
    dbref current;
    void *data;
{
  if (current == NOTHING)
    return (dbref) data;
  else
    return Next(current);
}

dbref
na_loc(current, data)
    dbref current;
    void *data;
{
  if (current == NOTHING)
    return (dbref) data;
  else if (current == (dbref) data)
    return Contents(current);
  else
    return Next(current);
}

dbref
na_nextbut(current, data)
    dbref current;
    void *data;
{
  dbref *dbrefs = data;

  do {
    if (current == NOTHING)
      current = dbrefs[0];
    else
      current = Next(current);
  } while (current == dbrefs[1]);
  return current;
}

dbref
na_except(current, data)
    dbref current;
    void *data;
{
  dbref *dbrefs = data;

  do {
    if (current == NOTHING)
      current = dbrefs[0];
    else if (current == dbrefs[0])
      current = Contents(current);
    else
      current = Next(current);
  } while (current == dbrefs[1]);
  return current;
}

dbref
na_except2(current, data)
    dbref current;
    void *data;
{
  dbref *dbrefs = data;

  do {
    if (current == NOTHING)
      current = dbrefs[0];
    else if (current == dbrefs[0])
      current = Contents(current);
    else
      current = Next(current);
  } while ((current == dbrefs[1]) || (current == dbrefs[2]));
  return current;
}


void
notify_anything(speaker, func, fdata, nsfunc, flags, message)
    dbref speaker;
    dbref (*func) ();
    void *fdata;
    void (*nsfunc) ();
    int flags;
    const char *message;
{
  dbref target;
  dbref passalong[3];
  struct notify_strings messages[5];
  struct notify_strings nospoofs[5];
  int i, j;
  struct descriptor_data *d;
  int poutput;
  char *pstring;
  char *bp;
  char tbuf1[BUFFER_LEN];
  ATTR *a;
  char const *asave, *ap;
  char *preserve[10];
  int havespoof = 0;
  char nospoof[BUFFER_LEN];
  char *wsave[10];

  if (!message || *message == '\0' || !func)
    return;

  /* Depth check */
  if (na_depth > 7)
    return;
  na_depth++;

  target = NOTHING;

  for (i = 0; i < 5; i++) {
    nospoofs[i].made = 0;
    messages[i].made = 0;
  }

  while ((target = func(target, fdata)) != NOTHING) {
    if ((flags & NA_PONLY) && (Typeof(target) != TYPE_PLAYER))
      continue;

    if (Typeof(target) == TYPE_PLAYER) {
      if (!Connected(target) && options.login_allow && under_limit)
	continue;
      for (d = descriptor_list; d; d = d->next) {
	if (d->connected && d->player == target) {
	  if (d->pueblo) {
	    poutput = NA_PUEBLO;
	  } else if (Toggles(target) & PLAYER_ANSI) {
#ifdef EXTENDED_ANSI
	    if (Toggles(target) & PLAYER_COLOR) {
	      poutput = NA_COLOR;
	    } else {
#endif
	      poutput = NA_ANSI;
#ifdef EXTENDED_ANSI
	    }
#endif
	  } else {
	    poutput = NA_PASCII;
	  }

	  if ((flags & NA_PONLY) && (poutput != NA_PUEBLO))
	    continue;

	  if (nsfunc && Nospoof(target) && (target != speaker)) {
	    if (!havespoof) {
	      nsfunc(nospoof, speaker, func, fdata);
	      havespoof = 1;
	    }
	    pstring = notify_makestring(nospoof, nospoofs, poutput);
	    queue_newwrite(d, pstring, strlen(pstring));
	  }
	  pstring = notify_makestring(message, messages, poutput);

	  if (pstring && *pstring)
	    queue_newwrite(d, pstring, strlen(pstring));
	  if (!(flags & NA_NOENTER)) {
	    if (poutput == NA_PUEBLO) {
	      if (flags & NA_NOPENTER)
		queue_newwrite(d, "\n", 1);
	      else
		queue_newwrite(d, "<BR>\n", 5);
	    } else {
	      queue_newwrite(d, "\r\n", 2);
	    }
	  }
	}
      }
    } else if (IS(target, TYPE_THING, THING_PUPPET) &&
	       (Location(target) != Location(Owner(target))) &&
	       ((flags & NA_PUPPET) || !(flags & NA_NORELAY))) {
      bp = tbuf1;
      safe_str(Name(target), tbuf1, &bp);
      safe_chr('>', tbuf1, &bp);
      safe_chr(' ', tbuf1, &bp);
      safe_str(message, tbuf1, &bp);
      *bp = 0;
      notify_anything(speaker, na_one, (void *) Owner(target), NULL, flags | NA_NORELAY, tbuf1);
    }
    if ((flags & NA_NOLISTEN)
	|| (!PLAYER_LISTEN && (Typeof(target) == TYPE_PLAYER))
	|| (Typeof(target) == TYPE_EXIT))
      continue;

    /* do @listen stuff */
    a = atr_get_noparent(target, "LISTEN");
    if (a) {
      strcpy(tbuf1, uncompress(a->value));
      if (wild_match(tbuf1, notify_makestring(message, messages, NA_ASCII))) {
#ifdef LISTEN_LOCK
	if (eval_lock(speaker, target, Listen_Lock)) {
#endif
	  if (PLAYER_AHEAR || (Typeof(target) != TYPE_PLAYER)) {
	    if (speaker != target)
	      charge_action(speaker, target, "AHEAR");
	    else
	      charge_action(speaker, target, "AMHEAR");
	    charge_action(speaker, target, "AAHEAR");
	  }
#ifdef LISTEN_LOCK
	}
#endif
	if ((speaker != target) && !(flags & NA_NORELAY) && !member(speaker, db[target].contents) &&
	    !filter_found(target, notify_makestring(message, messages, NA_ASCII), 1)) {
	  passalong[0] = target;
	  passalong[1] = target;
	  passalong[2] = Owner(target);
	  a = atr_get(target, "INPREFIX");
	  if (a) {
	    for (j = 0; j < 10; j++)
	      wsave[j] = wenv[j];
	    wenv[0] = (char *) message;
	    for (j = 1; j < 10; j++)
	      wenv[j] = NULL;
	    save_global_regs("inprefix_save", preserve);
	    asave = safe_uncompress(a->value);
	    ap = asave;
	    bp = tbuf1;
	    process_expression(tbuf1, &bp, &ap, target, speaker, speaker,
			       PE_DEFAULT, PT_DEFAULT, NULL);
	    if (bp != tbuf1)
	      safe_chr(' ', tbuf1, &bp);
	    safe_str(message, tbuf1, &bp);
	    *bp = 0;
	    free((Malloc_t) asave);
	    restore_global_regs("inprefix_save", preserve);
	    for (j = 0; j < 10; j++)
	      wenv[j] = wsave[j];
	  }
	  notify_anything(speaker, IS(target, TYPE_THING, THING_PUPPET) ? na_except2 : na_except,
	     passalong, NULL, flags | NA_NORELAY, (a) ? tbuf1 : message);
	}
      }
    }
    /* if object is flagged LISTENER, check for ^ listen patterns
     *    * these are like AHEAR - object cannot trigger itself.
     *    * unlike normal @listen, don't pass the message on.
     *    */

    if ((speaker != target) && (IS(target, TYPE_THING, THING_LISTEN) ||
				IS(target, TYPE_ROOM, ROOM_LISTEN))
#ifdef LISTEN_LOCK
	&& eval_lock(speaker, target, Listen_Lock)
#endif
      )
      atr_comm_match(target, speaker, '^', ':', notify_makestring(message, messages, NA_ASCII), 0);
  }
  na_depth--;
}

int
safe_tag(a_tag, buf, bp)
    char const *a_tag;
    char *buf;
    char **bp;
{
  int result = 0;
  if (SUPPORT_PUEBLO) {
    result += safe_chr(TAG_START, buf, bp);
    result += safe_str(a_tag, buf, bp);
    result += safe_chr(TAG_END, buf, bp);
  }
  return result;
}

int
safe_tag_cancel(a_tag, buf, bp)
    char const *a_tag;
    char *buf;
    char **bp;
{
  int result = 0;
  if (SUPPORT_PUEBLO) {
    result += safe_chr(TAG_START, buf, bp);
    result += safe_chr('/', buf, bp);
    result += safe_str(a_tag, buf, bp);
    result += safe_chr(TAG_END, buf, bp);
  }
  return result;
}

int
safe_tag_wrap(a_tag, params, data, buf, bp)
    char const *a_tag;
    char const *params;
    char const *data;
    char *buf;
    char **bp;
{
  int result = 0;
  if (SUPPORT_PUEBLO) {
    result += safe_chr(TAG_START, buf, bp);
    result += safe_str(a_tag, buf, bp);
    if (params && *params) {
      result += safe_chr(' ', buf, bp);
      result += safe_str(params, buf, bp);
    }
    result += safe_chr(TAG_END, buf, bp);
  }
  result += safe_str(data, buf, bp);
  if (SUPPORT_PUEBLO) {
    result += safe_tag_cancel(a_tag, buf, bp);
  }
  return result;
}

void
raw_notify(player, msg)
    dbref player;
    const char *msg;
{
  notify_anything(GOD, na_one, (void *) player, NULL, NA_NOLISTEN, msg);
}

#ifdef I_STDARG
void
flag_broadcast(object_flag_type inflags, object_flag_type intoggles, const char *fmt,...)
#else
void
flag_broadcast(inflags, intoggles, va_alist)
    object_flag_type inflags;
    object_flag_type intoggles;
    va_dcl
#endif				/* I_STDARG */
{
  /* takes flag/toggle masks, format string, and format args, and notifies
   * all connected players with that flag mask of something.
   * If inflags=intoggles=0, broadcast to all.
   * If one of them is 0, and the other isn't, then players
   * with _at least_ one of the bits in the flag mask are notified,
   * rather than players with that _entire_ flag mask. The former
   * behavior is more useful.
   * If both are nonzero, the player must have at least one bit in each.
   */

  va_list args;
  char tbuf1[BUFFER_LEN];
  DESC *d;
#ifndef I_STDARG
  char *fmt;

  va_start(args);
  fmt = va_arg(args, char *);
#else
  va_start(args, fmt);
#endif				/* I_STDARG */

  (void) vsprintf(tbuf1, fmt, args);
  va_end(args);

  DESC_ITER_CONN(d) {
    if (inflags) {
      if (intoggles) {
	if (!((Flags(d->player) & inflags) && (Toggles(d->player) & intoggles)))
	  continue;
      } else {
	if (!(Flags(d->player) & inflags))
	  continue;
      }
    } else {
      if (intoggles) {
	if (!(Toggles(d->player) & intoggles))
	  continue;
      }
    }
    queue_string(d, tbuf1);
    queue_write(d, "\r\n", 2);
    process_output(d);
  }
}


struct timeval
timeval_sub(now, then)
    struct timeval now;
    struct timeval then;
{
  now.tv_sec -= then.tv_sec;
  now.tv_usec -= then.tv_usec;
  if (now.tv_usec < 0) {
    now.tv_usec += 1000000;
    now.tv_sec--;
  }
  return now;
}

long
msec_diff(now, then)
    struct timeval now;
    struct timeval then;
{
  return ((now.tv_sec - then.tv_sec) * 1000
	  + (now.tv_usec - then.tv_usec) / 1000);
}

struct timeval
msec_add(t, x)
    struct timeval t;
    int x;
{
  t.tv_sec += x / 1000;
  t.tv_usec += (x % 1000) * 1000;
  if (t.tv_usec >= 1000000) {
    t.tv_sec += t.tv_usec / 1000000;
    t.tv_usec = t.tv_usec % 1000000;
  }
  return t;
}

struct timeval
update_quotas(last, current)
    struct timeval last;
    struct timeval current;
{
  int nslices;
  struct descriptor_data *d;
  nslices = (int) msec_diff(current, last) / COMMAND_TIME_MSEC;

  if (nslices > 0) {
    for (d = descriptor_list; d; d = d->next) {
      d->quota += COMMANDS_PER_TIME * nslices;
      if (d->quota > COMMAND_BURST_SIZE)
	d->quota = COMMAND_BURST_SIZE;
    }
  }
  return msec_add(last, nslices * COMMAND_TIME_MSEC);
}

#ifdef CAN_NEWSTYLE
void
shovechars(Port_t port)
#else
void
shovechars(port)
    Port_t port;
#endif
{
  /* this is the main game loop */

  fd_set input_set, output_set;
  time_t now;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval timeout, slice_timeout;
  int found;
  struct descriptor_data *d, *dnext;
#ifndef INFO_SLAVE
  struct descriptor_data *newd;
#endif
  int avail_descriptors;
#ifdef WIN32
  MMTIME win_time;
#endif
#ifdef INFO_SLAVE
  struct sockaddr_in addr;
  int addr_len;
  int newsock;
#endif

  if (!restarting) {
    sock = make_socket(port);
    maxd = sock + 1;
  }
#ifdef WIN32
  timeGetSystemTime(&win_time, sizeof(win_time));
  last_slice.tv_sec = win_time.u.ms / 1000;
  last_slice.tv_usec = (win_time.u.ms % 1000) *1000;
#else
  gettimeofday(&last_slice, (struct timezone *) 0);
#endif

  avail_descriptors = how_many_fds() - 4;
#ifdef INFO_SLAVE
  avail_descriptors -= 2;	/* reserve some more for setting up the slave */
  FD_ZERO(&info_pending);
#endif

  /* done. print message to the log */
  fprintf(stderr, "RESTART FINISHED.\n");
  fflush(stderr);
  while (shutdown_flag == 0) {
#ifdef WIN32
    timeGetSystemTime(&win_time, sizeof(win_time));
    current_time.tv_sec = win_time.u.ms / 1000;
    current_time.tv_usec = (win_time.u.ms % 1000) *1000;
#else
    gettimeofday(&current_time, (struct timezone *) 0);
#endif
    last_slice = update_quotas(last_slice, current_time);

    process_commands();

    if (shutdown_flag)
      break;

    /* test for events */
    dispatch();

#ifdef ALLOW_RPAGE
    /* anything received on our datagram remote page? */
    recv_rpage();
#endif				/* ALLOW_RPAGE */

    /* any queued robot commands waiting? */
    timeout.tv_sec = que_next();
    timeout.tv_usec = 0;
    next_slice = msec_add(last_slice, COMMAND_TIME_MSEC);
    slice_timeout = timeval_sub(next_slice, current_time);

    FD_ZERO(&input_set);
    FD_ZERO(&output_set);
    if (ndescriptors < avail_descriptors)
      FD_SET(sock, &input_set);
#ifdef INFO_SLAVE
    if (info_slave_state > 0)
      FD_SET(info_slave, &input_set);
#endif
    for (d = descriptor_list; d; d = d->next) {
      if (d->input.head)
	timeout = slice_timeout;
      else
	FD_SET(d->descriptor, &input_set);
      if (d->output.head)
	FD_SET(d->descriptor, &output_set);
    }

    if ((found = select(maxd, &input_set, &output_set,
			(fd_set *) 0, &timeout)) < 0) {
#ifdef WIN32
      if (found == SOCKET_ERROR && WSAGetLastError() != WSAEINTR)
#else
      if (errno != EINTR)
#endif
      {
	perror("select");
	return;
      }
#ifdef INFO_SLAVE
      now = time((time_t *) 0);
      if (info_slave_state == 2 && now > info_queue_time + 30) {
	/* rerun any pending queries that got lost */
	info_queue_time = now;
	for (newsock = 0; newsock < maxd; newsock++)
	  if (FD_ISSET(newsock, &info_pending))
	    query_info_slave(newsock);
      }
#endif
    } else {
      /* if !found then time for robot commands */

      if (!found) {
	do_top(options.queue_chunk);
	continue;
      } else {
	do_top(options.active_q_chunk);
      }
      now = time((time_t *) 0);
#ifdef INFO_SLAVE
      if (info_slave_state > 0 && FD_ISSET(info_slave, &input_set)) {
	if (info_slave_state == 1)
	  promote_info_slave();
	else
	  reap_info_slave();
      } else {
	if (info_slave_state == 2 && now > info_queue_time + 30) {
	  /* rerun any pending queries that got lost */
	  info_queue_time = now;
	  for (newsock = 0; newsock < maxd; newsock++)
	    if (FD_ISSET(newsock, &info_pending))
	      query_info_slave(newsock);
	}
      }
      if (FD_ISSET(sock, &input_set)) {
	addr_len = sizeof(addr);
	newsock = accept(sock, (struct sockaddr *) &addr, &addr_len);
	if (newsock < 0) {
#ifdef WIN32
	  if (newsock == INVALID_SOCKET && WSAGetLastError() != WSAEINTR &&
	      WSAGetLastError() != WSAEMFILE)
#else
	  if (errno && errno != EINTR && errno != EMFILE && errno != ENFILE)
#endif
	  {
	    perror("new_connection");
	    continue;		/* this should _not_ be return. */
	  }
	}
	ndescriptors++;
	query_info_slave(newsock);
	if (newsock >= maxd)
	  maxd = newsock + 1;
      }
#else
      if (FD_ISSET(sock, &input_set)) {
	if (!(newd = new_connection(sock))) {
#ifdef WIN32
	  if (newd == INVALID_SOCKET && WSAGetLastError() != WSAEINTR &&
	      WSAGetLastError() != WSAEMFILE)
#else
	  if (errno && errno != EINTR && errno != EMFILE && errno != ENFILE)
#endif
	  {
	    perror("new_connection");
	    continue;		/* this should _not_ be return. */
	  }
	} else {
	  ndescriptors++;
	  if (newd->descriptor >= maxd)
	    maxd = newd->descriptor + 1;
	}
      }
#endif
      for (d = descriptor_list; d; d = dnext) {
	dnext = d->next;
	if (FD_ISSET(d->descriptor, &input_set)) {
	  d->last_time = now;
	  if (!process_input(d)) {
	    shutdownsock(d);
	    continue;
	  }
	}
	if (FD_ISSET(d->descriptor, &output_set)) {
	  if (!process_output(d)) {
	    shutdownsock(d);
	  }
	}
      }
    }
  }
}

struct descriptor_data *
new_connection(oldsock)
    int oldsock;
{
  int newsock;
  struct sockaddr_in addr;
  int addr_len;
  char tbuf1[BUFFER_LEN];
  char *bp;
  char *socket_ident;
  char *chp;

  addr_len = sizeof(addr);
  newsock = accept(oldsock, (struct sockaddr *) &addr, &addr_len);
  if (newsock < 0) {
    return 0;
  }
  bp = tbuf1;
  if (USE_IDENT) {
    socket_ident = (char *) ident_id(newsock, options.ident_timeout);
    if (socket_ident) {
      /* Truncate at first non-printable character */
      for (chp = socket_ident; *chp && isprint(*chp); chp++) ;
      *chp = '\0';
      safe_str(socket_ident, tbuf1, &bp);
      safe_chr('@', tbuf1, &bp);
      free(socket_ident);
    }
  }
  safe_str((char *) hostname_convert(addr.sin_addr), tbuf1, &bp);
  *bp = '\0';
  if (Forbidden_Site(tbuf1)) {
    do_log(LT_CONN, 0, 0, "[%d/%s] Refused connection (remote port %d)",
	   newsock, tbuf1, ntohs(addr.sin_port));
    shutdown(newsock, 2);
    closesocket(newsock);
#ifndef WIN32
    errno = 0;
#endif
    return 0;
  }
  do_log(LT_CONN, 0, 0, "[%d/%s] Connection opened.", newsock, tbuf1);
  return initializesock(newsock, tbuf1);
}

void
clearstrings(d)
    struct descriptor_data *d;
{
  if (d->output_prefix) {
    mush_free((Malloc_t) d->output_prefix, "userstring");
    d->output_prefix = 0;
  }
  if (d->output_suffix) {
    mush_free((Malloc_t) d->output_suffix, "userstring");
    d->output_suffix = 0;
  }
}

void
fcache_dump(d, fp)
    DESC *d;
    FBLOCK *fp[];
{
  FBLOCK *fb;
  if (d->pueblo && fp[1]) {
    fb = fp[1];
    while (fb) {
      queue_newwrite(d, fb->data, fb->hdr.nchars);
      fb = fb->hdr.nxt;
    }
  } else {
    fb = fp[0];
    while (fb) {
      queue_write(d, fb->data, fb->hdr.nchars);
      fb = fb->hdr.nxt;
    }
  }
}


int
fcache_read(cp, filename)
    FBLOCK **cp;
    char *filename;
{
  int n, nmax, fd, tchars;
  char *bufp;
  FBLOCK *fp, *tfp;

  /* Free prior buffer chain */
  fp = *cp;
  while (fp != NULL) {
    tfp = fp->hdr.nxt;
    free(fp);
    fp = tfp;
  }
  *cp = NULL;

  /* Read the text file into a new chain */
  close(reserved);
  if ((fd = open(filename, O_RDONLY, 0)) == -1) {
    do_log(LT_ERR, 0, 0, "couldn't open cached text file '%s'", filename);
    reserved = open("/dev/null", O_RDWR);
    return -1;
  }
  fp = (FBLOCK *) malloc(sizeof(char) * 256);
  fp->hdr.nxt = NULL;
  fp->hdr.nchars = 0;
  *cp = fp;
  tfp = NULL;
  tchars = 0;

  /* Read in the first chunk of the file */
  nmax = FBLOCK_SIZE;
  bufp = fp->data;
  n = read(fd, bufp, nmax);
  while (n > 0) {
    /* if we didn't read in all we wanted, update the pointers and try to
     * fill the current buffer.
     */
    fp->hdr.nchars += n;
    tchars += n;
    if ((Size_t) fp->hdr.nchars < FBLOCK_SIZE) {
      nmax -= n;
      bufp += n;
    } else {
      /* filled the current buffer. Get a new one. */
      tfp = fp;
      fp = (FBLOCK *) malloc(sizeof(char) * 256);
      fp->hdr.nxt = NULL;
      fp->hdr.nchars = 0;
      tfp->hdr.nxt = fp;
      nmax = FBLOCK_SIZE;
      bufp = fp->data;
    }
    /* read in the next chunk of the file */
    n = read(fd, bufp, nmax);
  }
  close(fd);
  reserved = open("/dev/null", O_RDWR);
  if (fp->hdr.nchars == 0) {
    free(fp);
    if (tfp == NULL)
      *cp = NULL;
    else
      tfp->hdr.nxt = NULL;
  }
  return tchars;
}

void
fcache_load(player)
    dbref player;
{
  int conn, motd, wiz, new, reg, quit, down, full;
  int guest;
  int i;

  for (i = 0; i < (SUPPORT_PUEBLO ? 2 : 1); i++) {
    conn = fcache_read(&options.connect_fcache[i], options.connect_file[i]);
    motd = fcache_read(&options.motd_fcache[i], options.motd_file[i]);
    wiz = fcache_read(&options.wizmotd_fcache[i], options.wizmotd_file[i]);
    new = fcache_read(&options.newuser_fcache[i], options.newuser_file[i]);
    reg = fcache_read(&options.register_fcache[i], options.register_file[i]);
    quit = fcache_read(&options.quit_fcache[i], options.quit_file[i]);
    down = fcache_read(&options.down_fcache[i], options.down_file[i]);
    full = fcache_read(&options.full_fcache[i], options.full_file[i]);
    guest = fcache_read(&options.guest_fcache[i], options.guest_file[i]);

    if (player != NOTHING) {
      notify(player,
	     tprintf("%s sizes:  NewUser...%d  Connect...%d  Guest...%d  Motd...%d  Wizmotd...%d  Quit...%d  Register...%d  Down...%d  Full...%d",
		     i ? "HTMLFile" : "File",
		     new, conn, guest, motd, wiz, quit, reg, down, full));
    }
  }

}

void
fcache_init()
{
  int i;
  for (i = 0; i < 2; i++) {
    options.connect_fcache[i] = NULL;
    options.motd_fcache[i] = NULL;
    options.wizmotd_fcache[i] = NULL;
    options.newuser_fcache[i] = NULL;
    options.register_fcache[i] = NULL;
    options.quit_fcache[i] = NULL;
    options.down_fcache[i] = NULL;
    options.full_fcache[i] = NULL;
    options.guest_fcache[i] = NULL;
  }
  fcache_load(NOTHING);
}

void
logout_sock(d)
    struct descriptor_data *d;
{
  if (d->connected) {
    fcache_dump(d, options.quit_fcache);
    do_log(LT_CONN, 0, 0, "[%d/%s] Logout by %s(#%d) <Connection not dropped>",
	   d->descriptor, d->addr, Name(d->player), d->player);
    fflush(connlog_fp);
    announce_disconnect(d->player);
#ifdef USE_MAILER
    do_mail_purge(d->player);
#endif
    if (MAX_LOGINS) {
      login_number--;
      if (!under_limit && (login_number < MAX_LOGINS)) {
	under_limit = 1;
	do_log(LT_CONN, 0, 0,
	       "Below maximum player limit of %d. Logins enabled.",
	       MAX_LOGINS);
      }
    }
  } else {
    do_log(LT_CONN, 0, 0,
	   "[%d/%s] Logout, never connected. <Connection not dropped>",
	   d->descriptor, d->addr);
  }
  process_output(d);		/* flush our old output */
  /* pretend we have a new connection */
  d->connected = 0;
  d->output_prefix = 0;
  d->output_suffix = 0;
  d->output_size = 0;
  d->output.head = 0;
  d->player = 0;
  d->output.tail = &d->output.head;
  d->input.head = 0;
  d->input.tail = &d->input.head;
  d->raw_input = 0;
  d->raw_input_at = 0;
  d->quota = COMMAND_BURST_SIZE;
  d->last_time = 0;
  d->cmds = 0;
  d->hide = 0;
  d->doing[0] = '\0';
#ifdef USE_MAILER
  d->mailp = NULL;
#endif
  welcome_user(d);
}

void
shutdownsock(d)
    struct descriptor_data *d;
{
  if (d->connected) {
    do_log(LT_CONN, 0, 0, "[%d/%s] Logout by %s(#%d)",
	   d->descriptor, d->addr, Name(d->player), d->player);
    fflush(connlog_fp);
    if (d->connected != 2) {
      fcache_dump(d, options.quit_fcache);
      /* Player was not allowed to log in from the connect screen */
      announce_disconnect(d->player);
#ifdef USE_MAILER
      do_mail_purge(d->player);
#endif
    }
    if (MAX_LOGINS) {
      login_number--;
      if (!under_limit && (login_number < MAX_LOGINS)) {
	under_limit = 1;
	do_log(LT_CONN, 0, 0,
	       "Below maximum player limit of %d. Logins enabled.",
	       MAX_LOGINS);
      }
    }
  } else {
    do_log(LT_CONN, 0, 0, "[%d/%s] Connection closed, never connected.",
	   d->descriptor, d->addr);
  }
  process_output(d);
  clearstrings(d);
  shutdown(d->descriptor, 2);
  closesocket(d->descriptor);
  freeqs(d);
  if (d->prev)
    d->prev->next = d->next;
  else				/* d was the first one! */
    descriptor_list = d->next;
  if (d->next)
    d->next->prev = d->prev;
  mush_free((Malloc_t) d, "descriptor");
  ndescriptors--;
}

struct descriptor_data *
initializesock(s, addr)
    int s;
    char *addr;
{
  DESC *d;
  d = (DESC *) mush_malloc(sizeof(DESC), "descriptor");
  if (!d)
    panic("Out of memory.");
  d->descriptor = s;
  d->connected = 0;
  make_nonblocking(s);
  d->output_prefix = 0;
  d->output_suffix = 0;
  d->output_size = 0;
  d->output.head = 0;
  d->player = 0;
  d->output.tail = &d->output.head;
  d->input.head = 0;
  d->input.tail = &d->input.head;
  d->raw_input = 0;
  d->raw_input_at = 0;
  d->quota = COMMAND_BURST_SIZE;
  d->last_time = 0;
  d->cmds = 0;
  d->hide = 0;
  d->doing[0] = '\0';
#ifdef USE_MAILER
  /* Go find where this player's messages start */
  d->mailp = NULL;
#endif
  strncpy(d->addr, addr, 100);
  d->addr[99] = '\0';
  d->pueblo = 0;
  if (descriptor_list)
    descriptor_list->prev = d;
  d->next = descriptor_list;
  d->prev = NULL;
  descriptor_list = d;
  welcome_user(d);
  return d;
}

#ifdef CAN_NEWSTYLE
int
make_socket(Port_t port)
#else
int
make_socket(port)
    Port_t port;
#endif
{
  int s;
  struct sockaddr_in server;
  int opt;
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0) {
    perror("creating stream socket");
    exit(3);
  }
  opt = 1;
  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
		 (char *) &opt, sizeof(opt)) < 0) {
    perror("setsockopt");
    exit(1);
  }
  server.sin_family = AF_INET;
  if (*MUSH_IP_ADDR)
    server.sin_addr.s_addr = inet_addr(MUSH_IP_ADDR);
  else
    server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons(port);
  if (bind(s, (struct sockaddr *) &server, sizeof(server))) {
    perror("binding stream socket");
    closesocket(s);
    exit(4);
  }
  fprintf(stderr, "Listening on port %d\n", port);
  listen(s, 5);
  return s;
}

#ifdef INFO_SLAVE
static void
make_info_slave()
{
  struct sockaddr_in addr;
  int opt;
  Pid_t child;
  char num[10];

  if (info_slave_state != 0) {
    closesocket(info_slave);
    kill(info_slave_pid, 15);
    info_slave_state = 0;
  }
  info_slave = socket(AF_INET, SOCK_STREAM, 0);
  if (info_slave < 0) {
    perror("creating slave ear stream socket");
    return;
  }
  opt = 1;
  if (setsockopt(info_slave, SOL_SOCKET, SO_REUSEADDR,
		 (char *) &opt, sizeof(opt)) < 0) {
    perror("setsockopt");
    return;
  }
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = INADDR_ANY;
  addr.sin_port = htons(0);
  if (bind(info_slave, (struct sockaddr *) &addr, sizeof(addr))) {
    perror("binding slave ear stream socket");
    closesocket(info_slave);
    return;
  }
  opt = sizeof(addr);
  if (getsockname(info_slave, (struct sockaddr *) &addr, &opt) < 0) {
    perror("getting address of slave ear stream socket");
    closesocket(info_slave);
    return;
  }
  listen(info_slave, 1);
  child = fork();
  if (child < 0) {
    perror("forking info slave");
    closesocket(info_slave);
    return;
  }
  if (child) {
    info_slave_state = 1;
    info_slave_pid = child;
    do_rawlog(LT_ERR, "Spawning info slave on port %d, pid %d",
	      ntohs(addr.sin_port), child);
  } else {
    sprintf(num, "%d", ntohs(addr.sin_port));
    execl("./info_slave", "./info_slave", num, (char *) NULL);
    perror("execing info slave");
    exit(1);
  }
  info_query_spill = 0;
  info_reap_spill = 0;
  if (info_slave >= maxd)
    maxd = info_slave + 1;
}

static void
promote_info_slave()
{
  int newsock;
  struct sockaddr_in addr;
  int addr_len;
  int j;

  if (info_slave_state != 1) {
    make_info_slave();
    return;
  }
  addr_len = sizeof(addr);
  newsock = accept(info_slave, (struct sockaddr *) &addr, &addr_len);
  if (newsock < 0) {
    perror("accepting info slave connection");
    make_info_slave();
    return;
  }
  closesocket(info_slave);
  info_slave = newsock;
  make_nonblocking(info_slave);
  /* Do authentication here, if we care */
  info_slave_state = 2;
  do_rawlog(LT_ERR, "Accepted info slave from port %d", ntohs(addr.sin_port));
  for (j = 0; j < maxd; j++)
    if (FD_ISSET(j, &info_pending))
      query_info_slave(j);
  if (info_slave >= maxd)
    maxd = info_slave + 1;
}

static void
query_info_slave(fd)
    int fd;
{
  int len, size;
  static char buf[100];		/* overkill */
  struct sockaddr_in addr;

  FD_SET(fd, &info_pending);
  info_queue_time = time((time_t *) NULL);

  if (info_slave_state != 2) {
    make_info_slave();
    return;
  }
  /* THIS PACKET IS _NOT_ IN NETWORK ORDER */
  size = info_query_spill;	/* cleanup for truncated packet */
  memset(buf, 0, size);
  info_query_spill = 0;
  len = sizeof(addr);
  if (getpeername(fd, (struct sockaddr *) &addr, &len) < 0) {
    perror("socket peer vanished");
    shutdown(fd, 2);
    closesocket(fd);
    FD_CLR(fd, &info_pending);
    return;
  }
  memcpy(buf + size, &addr.sin_addr, sizeof(addr.sin_addr));
  size += sizeof(addr.sin_addr);
  memcpy(buf + size, &addr.sin_port, sizeof(addr.sin_port));
  size += sizeof(addr.sin_port);
  len = sizeof(addr);
  if (getsockname(fd, (struct sockaddr *) &addr, &len) < 0) {
    perror("socket self vanished");
    shutdown(fd, 2);
    closesocket(fd);
    FD_CLR(fd, &info_pending);
    return;
  }
  memcpy(buf + size, &addr.sin_addr, sizeof(addr.sin_addr));
  size += sizeof(addr.sin_addr);
  memcpy(buf + size, &addr.sin_port, sizeof(addr.sin_port));
  size += sizeof(addr.sin_port);
  memcpy(buf + size, &fd, sizeof(fd));
  size += sizeof(fd);

#ifdef WIN32
  len = send(info_slave, buf, size, 0);
#else
  len = write(info_slave, buf, size);
#endif
  if (len < size) {
    perror("info slave query");
#ifdef WIN32
    if (len == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
#else
    if (errno != EWOULDBLOCK)
#endif
      make_info_slave();
    else
      /* drop partial packet on floor.  cleanup later. */
      info_query_spill = size - len;
  }
}

static void
reap_info_slave()
{
  int fd, len, size;
  static char buf[10000];	/* overkill */

  if (info_slave_state != 2) {
    make_info_slave();
    return;
  }
  if (info_reap_spill) {
    /* clean up some mess */
#ifdef WIN32
    len = recv(info_slave, buf, info_reap_spill, 0);
#else
    len = read(info_slave, buf, info_reap_spill);
#endif
    info_reap_spill -= len;
    if (len < 1) {
      /* crap.  lost the slave. */
      perror("info slave reap spill");
      make_info_slave();
      return;
    }
    if (info_reap_spill)
      /* can't read enough, come back later */
      return;
  }
  for (;;) {
    /* THIS PACKET IS _NOT_ IN NETWORK ORDER */
    /* grab the fd */
#ifdef WIN32
    len = recv(info_slave, (char *) &fd, sizeof(int), 0);
#else
    len = read(info_slave, (char *) &fd, sizeof(int));
#endif
#ifdef WIN32
    if (len != SOCKET_ERROR || WSAGetLastError() != WSAEWOULDBLOCK)
#else
    if (len < 0 && errno == EWOULDBLOCK)
#endif
      /* got all the data */
      return;
    if ((Size_t) len < sizeof(int)) {
      /* we're hosed now */
      perror("info slave reap fd");
      make_info_slave();
      return;
    }
    /* grab the string size */
#ifdef WIN32
    len = recv(info_slave, (char *) &size, sizeof(int), 0);
#else
    len = read(info_slave, (char *) &size, sizeof(int));
#endif
    if ((Size_t) len < sizeof(int) || (Size_t) size > sizeof(buf)) {
      /* we're still hosed */
      perror("info slave reap size");
      make_info_slave();
      return;
    }
    /* grab the actual string */
#ifdef WIN32
    len = recv(info_slave, buf, size, 0);
#else
    len = read(info_slave, buf, size);
#endif
    buf[len] = '\0';
    if (len < size) {
      /* crap... lost some.  clean up the mess and requery */
      info_reap_spill = size - len;
      query_info_slave(fd);
      return;
    }
    /* okay, now we have some info! */
    if (!FD_ISSET(fd, &info_pending))
      /* got a bloody duplicate */
      return;
    FD_CLR(fd, &info_pending);
    if (Forbidden_Site(buf)) {
      do_log(LT_CONN, 0, 0, "[%d/%s] Refused connection.", fd, buf);
      shutdown(fd, 2);
      closesocket(fd);
#ifndef WIN32
      errno = 0;
#endif
      return;
    }
    do_log(LT_CONN, 0, 0, "[%d/%s] Connection opened.", fd, buf);
    (void) initializesock(fd, buf);
  }
}

void
kill_info_slave()
{
  if (info_slave_state != 0) {
    closesocket(info_slave);
    kill(info_slave_pid, 15);
    info_slave_state = 0;
  }
}
#endif

struct text_block *
make_text_block(s, n)
    const char *s;
    int n;
{
  struct text_block *p;
  p = (struct text_block *) mush_malloc(sizeof(struct text_block), "text_block");
  if (!p)
    panic("Out of memory");
  p->buf = (char *) mush_malloc(sizeof(char) * n, "text_block_buff");
  if (!p->buf)
    panic("Out of memory");

  memcpy(p->buf, s, n);
  p->nchars = n;
  p->start = p->buf;
  p->nxt = 0;
  return p;
}

void
free_text_block(t)
    struct text_block *t;
{
  if (t) {
    if (t->buf)
      mush_free((Malloc_t) t->buf, "text_block_buff");
    mush_free((Malloc_t) t, "text_block");
  }
}

void
add_to_queue(q, b, n)
    struct text_queue *q;
    const char *b;
    int n;
{
  struct text_block *p;
  if (n == 0)
    return;

  p = make_text_block(b, n);
  p->nxt = 0;
  *q->tail = p;
  q->tail = &p->nxt;
}

int
flush_queue(q, n)
    struct text_queue *q;
    int n;
{
  struct text_block *p;
  int really_flushed = 0;
  n += strlen(flushed_message);

  while (n > 0 && (p = q->head)) {
    n -= p->nchars;
    really_flushed += p->nchars;
    q->head = p->nxt;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 1.\n", p);
#endif				/* DEBUG */
    free_text_block(p);
  }
  p = make_text_block(flushed_message, strlen(flushed_message));
  p->nxt = q->head;
  q->head = p;
  if (!p->nxt)
    q->tail = &p->nxt;
  really_flushed -= p->nchars;
  return really_flushed;
}

int
queue_write(d, b, n)
    struct descriptor_data *d;
    const char *b;
    int n;
{
  char buff[BUFFER_LEN];
  struct notify_strings messages[5];
  char *s;
  PUEBLOBUFF;

  if ((n == 2) && (b[0] == '\r') && (b[1] == '\n')) {
    if (d->pueblo)
      queue_newwrite(d, "<BR>\n", 5);
    else
      queue_newwrite(d, b, 2);
    return n;
  }
  if (n > BUFFER_LEN)
    n = BUFFER_LEN;

  strncpy(buff, b, n);
  buff[n] = '\0';

  if (d->pueblo) {
    PUSE;
    tag_wrap("PRE", NULL, buff);
    PEND;
    messages[NA_PUEBLO].made = 0;
    s = notify_makestring(pbuff, messages, NA_PUEBLO);
  } else {
    messages[NA_COLOR].made = 0;
    s = notify_makestring(buff, messages, NA_COLOR);
  }
  queue_newwrite(d, s, strlen(s));
  return n;
}

int
queue_newwrite(d, b, n)
    struct descriptor_data *d;
    const char *b;
    int n;
{
  int space;
  space = MAX_OUTPUT - d->output_size - n;
  if (space < SPILLOVER_THRESHOLD) {
    process_output(d);
    space = MAX_OUTPUT - d->output_size - n;
    if (space < 0)
      d->output_size -= flush_queue(&d->output, -space);
  }
  add_to_queue(&d->output, b, n);
  d->output_size += n;
  return n;
}

int
queue_string(d, s)
    struct descriptor_data *d;
    const char *s;
{
  char *n;
  int poutput;
  struct notify_strings messages[5];
  dbref target;

  for (poutput = 0; poutput < 5; poutput++)
    messages[poutput].made = 0;

  target = d->player;

  if (d->pueblo) {
    poutput = NA_PUEBLO;
  } else if (Toggles(target) & PLAYER_ANSI) {
#ifdef EXTENDED_ANSI
    if (Toggles(target) & PLAYER_COLOR) {
      poutput = NA_COLOR;
    } else {
#endif
      poutput = NA_ANSI;
#ifdef EXTENDED_ANSI
    }
#endif
  } else {
    poutput = NA_PASCII;
  }
  n = notify_makestring(s, messages, poutput);
  return queue_newwrite(d, n, strlen(n));
}

int
process_output(d)
    struct descriptor_data *d;
{
  struct text_block **qp, *cur;
  int cnt;
  for (qp = &d->output.head; ((cur = *qp) != NULL);) {
#ifdef WIN32
    cnt = send(d->descriptor, cur->start, cur->nchars, 0);	/* NJG  */
#else
    cnt = write(d->descriptor, cur->start, cur->nchars);
#endif
    if (cnt < 0) {
#ifdef WIN32
      if (cnt == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
#else
#ifdef EAGAIN
      if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
#else
      if (errno == EWOULDBLOCK)
#endif
#endif
	return 1;
      return 0;
    }
    d->output_size -= cnt;
    if (cnt == cur->nchars) {
      if (!cur->nxt)
	d->output.tail = qp;
      *qp = cur->nxt;
#ifdef DEBUG
      fprintf(stderr, "free_text_block(0x%x) at 2.\n", cur);
#endif				/* DEBUG */
      free_text_block(cur);
      continue;			/* do not adv ptr */
    }
    cur->nchars -= cnt;
    cur->start += cnt;
    break;
  }
  return 1;
}

void
make_nonblocking(s)
    int s;
{
#ifdef WIN32
  unsigned long arg = 1;
  if (ioctlsocket(s, FIONBIO, &arg) == -1) {
#else
  if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
#endif
    perror("make_nonblocking: fcntl");
    panic("O_NDELAY fcntl failed");
  }
}

void
freeqs(d)
    struct descriptor_data *d;
{
  struct text_block *cur, *next;
  cur = d->output.head;
  while (cur) {
    next = cur->nxt;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 3.\n", cur);
#endif				/* DEBUG */
    free_text_block(cur);
    cur = next;
  }
  d->output.head = 0;
  d->output.tail = &d->output.head;

  cur = d->input.head;
  while (cur) {
    next = cur->nxt;
#ifdef DEBUG
    fprintf(stderr, "free_text_block(0x%x) at 4.\n", cur);
#endif				/* DEBUG */
    free_text_block(cur);
    cur = next;
  }
  d->input.head = 0;
  d->input.tail = &d->input.head;

  if (d->raw_input) {
    mush_free((Malloc_t) d->raw_input, "descriptor_raw_input");
  }
  d->raw_input = 0;
  d->raw_input_at = 0;
}

void
welcome_user(d)
    struct descriptor_data *d;
{
  if (SUPPORT_PUEBLO && !d->pueblo)
    queue_newwrite(d, PUEBLO_HELLO, strlen(PUEBLO_HELLO));
  fcache_dump(d, options.connect_fcache);
}

void
do_new_spitfile(player, arg1, index_file, text_file, restricted)
    dbref player;
    char *arg1;
    char *index_file;
    char *text_file;
    int restricted;		/* 1 = admin help, 0 = regular help */
{
  int help_found;
  help_indx entry;
  FILE *fp;
  char *p, line[LINE_SIZE + 1];
  char the_topic[LINE_SIZE + 2];

  if (!text_file || !*text_file)
    return;
  if (*arg1 == '\0')
    arg1 = (char *) "help";
  else if (*arg1 == '&') {
    notify(player, "Help topics don't start with '&'.");
    return;
  }
  if (strlen(arg1) > LINE_SIZE)
    *(arg1 + LINE_SIZE) = '\0';

  if (restricted) {
    if (!Hasprivs(player)) {
      notify(player, "You don't look like an admin to me.");
      return;
    }
    sprintf(the_topic, "&%s", arg1);
  } else
    strcpy(the_topic, arg1);

  if ((fp = fopen(index_file, "rb")) == NULL) {
    notify(player, "Sorry, that function is temporarily unavailable.");
    do_log(LT_ERR, 0, 0, "Can't open index file %s for reading", index_file);
    return;
  }
  while ((help_found = fread(&entry, sizeof(help_indx), 1, fp)) == 1)
    if (string_prefix(entry.topic, the_topic))
       break;
  fclose(fp);
  if (!help_found) {
    notify(player, tprintf("No entry for '%s'.", arg1));
    return;
  }
  if ((fp = fopen(text_file, "r")) == NULL) {
    notify(player, "Sorry, that function is temporarily unavailable.");
    do_log(LT_ERR, 0, 0, "Can't open text file %s for reading", text_file);
    return;
  }
  if (fseek(fp, entry.pos, 0) < 0L) {
    notify(player, "Sorry, that function is temporarily unavailable.");
    do_log(LT_ERR, 0, 0, "Seek error in file %s\n", text_file);
    return;
  }
  strcpy(the_topic, strupper(entry.topic + (*entry.topic == '&')));
  /* ANSI topics */
  if (ShowAnsi(player)) {
    char ansi_topic[LINE_SIZE + 10];
    sprintf(ansi_topic, "%s%s%s", ANSI_HILITE, the_topic, ANSI_NORMAL);
    notify(player, ansi_topic);
  } else
    notify(player, the_topic);

  if (SUPPORT_PUEBLO)
    notify_noenter(player, tprintf("%cPRE%c", TAG_START, TAG_END));
  for (;;) {
    if (fgets(line, LINE_SIZE, fp) == NULL)
      break;
    if (line[0] == '&')
      break;
    if (line[0] == '\n') {
      notify(player, " ");
    } else {
      for (p = line; *p != '\0'; p++)
	if (*p == '\n')
	  *p = '\0';
      notify(player, line);
    }
  }
  if (SUPPORT_PUEBLO)
    notify(player, tprintf("%c/PRE%c", TAG_START, TAG_END));
  fclose(fp);
}


void
save_command(d, command)
    struct descriptor_data *d;
    const char *command;
{
  add_to_queue(&d->input, command, strlen(command) + 1);
}

int
process_input(d)
    struct descriptor_data *d;
{
  int got;
  char *p, *pend, *q, *qend;
  char tbuf1[BUFFER_LEN];

#ifdef WIN32
  got = recv(d->descriptor, tbuf1, sizeof tbuf1, 0);
#else
  got = read(d->descriptor, tbuf1, sizeof tbuf1);
#endif
  if (got <= 0)
    return 0;
  if (!d->raw_input) {
    d->raw_input = (char *) mush_malloc(sizeof(char) * MAX_COMMAND_LEN, "descriptor_raw_input");
    if (!d->raw_input)
      panic("Out of memory");
    d->raw_input_at = d->raw_input;
  }
  p = d->raw_input_at;
  pend = d->raw_input + MAX_COMMAND_LEN - 1;
  for (q = tbuf1, qend = tbuf1 + got; q < qend; q++) {
    if (*q == '\n') {
      *p = '\0';
      if (p > d->raw_input)
	save_command(d, d->raw_input);
      p = d->raw_input;
    } else if (*q == '\b') {
      if (p > d->raw_input)
	p--;
    } else if (p < pend &&
#if (COMPRESSION_TYPE > 1)
	       isascii(*q) && isprint(*q)
#else
	       *q >= ' '
#endif
      ) {
      *p++ = *q;
    }
  }
  if (p > d->raw_input) {
    d->raw_input_at = p;
  } else {
    mush_free((Malloc_t) d->raw_input, "descriptor_raw_input");
    d->raw_input = 0;
    d->raw_input_at = 0;
  }
  return 1;
}

void
set_userstring(userstring, command)
    char **userstring;
    const char *command;
{
  if (*userstring) {
    mush_free((Malloc_t) * userstring, "userstring");
    *userstring = NULL;
  }
  while (*command && isascii(*command) && isspace(*command))
    command++;
  if (*command)
    *userstring = strdup(command);
}

void
process_commands()
{
  int nprocessed;
  DESC *dnext;
  struct text_block *t;
  int retval = 1;

  do {
    nprocessed = 0;
    for (cdesc = descriptor_list; cdesc;
	 cdesc = (nprocessed > 0 && retval > 0) ? cdesc->next : dnext) {
      dnext = cdesc->next;
      if (cdesc->quota > 0 && (t = cdesc->input.head)) {
	cdesc->quota--;
	nprocessed++;
	retval = do_command(cdesc, t->start);
	if (retval == 0) {
	  shutdownsock(cdesc);
	} else if (retval == -1) {
	  logout_sock(cdesc);
	} else {
	  cdesc->input.head = t->nxt;
	  if (!cdesc->input.head)
	    cdesc->input.tail = &cdesc->input.head;
	  if (t) {
#ifdef DEBUG
	    fprintf(stderr, "free_text_block(0x%x) at 5.\n", t);
#endif				/* DEBUG */
	    free_text_block(t);
	  }
	}
      }
    }
  } while (nprocessed > 0);
  cdesc = (DESC *) NULL;
}

#define send_prefix(d) \
  if (d->output_prefix) { \
    queue_newwrite(d, d->output_prefix, strlen(d->output_prefix)); \
    queue_newwrite(d, "\r\n", 2); \
  }

#define send_suffix(d) \
  if (d->output_suffix) { \
    queue_newwrite(d, d->output_suffix, strlen(d->output_suffix)); \
    queue_newwrite(d, "\r\n", 2); \
  }

int
do_command(d, command)
    struct descriptor_data *d;
    char *command;
{
  int j;

  depth = 0;
  (d->cmds)++;

  if (!strcmp(command, QUIT_COMMAND)) {
    return 0;
  } else if (!strcmp(command, LOGOUT_COMMAND)) {
    return -1;
  } else if (!strcmp(command, INFO_COMMAND)) {
    send_prefix(d);
    dump_info(d);
    send_suffix(d);
  } else if (!strncmp(command, WHO_COMMAND, strlen(WHO_COMMAND))) {
    send_prefix(d);
    dump_users(d, command + strlen(WHO_COMMAND), 0);
    send_suffix(d);
  } else if (!strncmp(command, DOING_COMMAND, strlen(DOING_COMMAND))) {
    send_prefix(d);
    dump_users(d, command + strlen(DOING_COMMAND), 1);
    send_suffix(d);
  } else if (!strncmp(command, PREFIX_COMMAND, strlen(PREFIX_COMMAND))) {
    set_userstring(&d->output_prefix, command + strlen(PREFIX_COMMAND));
  } else if (!strncmp(command, SUFFIX_COMMAND, strlen(SUFFIX_COMMAND))) {
    set_userstring(&d->output_suffix, command + strlen(SUFFIX_COMMAND));
  } else if (SUPPORT_PUEBLO && !strncmp(command, PUEBLO_COMMAND, strlen(PUEBLO_COMMAND))) {
    if (!d->pueblo) {
      queue_newwrite(d, PUEBLO_SEND, strlen(PUEBLO_SEND));
      do_log(LT_CONN, 0, 0, "[%d/%s] Switching to Pueblo mode.",
	     d->descriptor, d->addr);
      d->pueblo = 1;
      if (!d->connected)
	welcome_user(d);
    }
  } else {
    if (d->connected) {
      send_prefix(d);
      cplr = d->player;
      strcpy(ccom, command);

      /* Clear %0-%9 and r(0) - r(9) */
      for (j = 0; j < 10; j++) {
	wenv[j] = (char *) NULL;
	renv[j][0] = '\0';
      }

      process_command(d->player, command, d->player, 1);
      send_suffix(d);
    } else {
      if (!check_connect(d, command))
	return 0;
    }
  }
  return 1;
}

static int
dump_messages(d, player, new)
    DESC *d;
    dbref player;
    int new;


  /* 0 if connect, 1 if create */
{
  d->connected = 1;
  d->connected_at = time((time_t *) 0);
  d->player = player;
  d->doing[0] = '\0';

  if (MAX_LOGINS) {
    /* check for exceeding max player limit */
    login_number++;
    if (under_limit && (login_number > MAX_LOGINS)) {
      under_limit = 0;
      fprintf(connlog_fp,
	      "Limit of %d players reached. Logins disabled.\n",
	      MAX_LOGINS);
      fflush(connlog_fp);
    }
  }
  /* give players a message on connection */
  if (!options.login_allow || !under_limit ||
      (Guest(player) && !options.guest_allow)) {
    if (!options.login_allow) {
      raw_notify(player, asterisk_line);
      fcache_dump(d, options.down_fcache);
      if (cf_downmotd_msg && *cf_downmotd_msg)
	raw_notify(player, cf_downmotd_msg);
    } else if (MAX_LOGINS && !under_limit) {
      raw_notify(player, asterisk_line);
      fcache_dump(d, options.full_fcache);
      if (cf_fullmotd_msg && *cf_fullmotd_msg)
	raw_notify(player, cf_fullmotd_msg);
    }
    if (!Can_Login(player)) {
      raw_notify(player, asterisk_line);
      /* when the connection has been refused, we want to update the
       * LASTFAILED info on the player
       */
      check_lastfailed(player, d->addr);
      return 0;
    } else
      raw_notify(player, asterisk_line);
  }
#ifdef USE_MAILER
  d->mailp = find_exact_starting_point(player);
#endif

  /* give permanent text messages */
  if (new)
    fcache_dump(d, options.newuser_fcache);
  fcache_dump(d, options.motd_fcache);
  if (Hasprivs(player))
    fcache_dump(d, options.wizmotd_fcache);
  if (Guest(player))
    fcache_dump(d, options.guest_fcache);

#ifdef CREATION_TIMES
  if (ModTime(player))
    notify(player, tprintf("%d failed connections since last login.",
			   ModTime(player)));
  ModTime(player) = (time_t) 0;
#endif
  announce_connect(player);	/* broadcast connect message */
  check_last(player, d->addr);	/* set Last, Lastsite, give paycheck */
#ifdef USE_MAILER
  /* Check folder 0, not silently (i.e. Report lack of mail, too) */
  queue_write(d, "\r\n", 2);
  check_mail(player, 0, 0);
  set_player_folder(player, 0);
#endif
  do_look_around(player);
  if (Haven(player)) {
    notify(player, "Your HAVEN flag is set. You cannot receive pages.");
  }
#ifdef VACATION_FLAG
  if (Vacation(player)) {
    notify(player, "Welcome back from vacation!");
    set_flag(player, player, (char *) "ON-VACATION", 1, 0, 0);
  }
#endif
  return 1;
}

int
check_connect(d, msg)
    struct descriptor_data *d;
    const char *msg;
{
  char command[MAX_COMMAND_LEN];
  char user[MAX_COMMAND_LEN];
  char password[MAX_COMMAND_LEN];
  dbref player;

  parse_connect(msg, command, user, password);

  if (string_prefix("connect", command)) {
    if ((player = connect_player(user, password, d->addr)) == NOTHING) {
      queue_string(d, connect_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed connect to '%s'.",
	     d->descriptor, d->addr, user);
    } else {
      do_log(LT_CONN, 0, 0, "[%d/%s] Connected to %s(#%d) in %s(#%d)",
	     d->descriptor, d->addr, Name(player), player,
	     Name(Location(player)), Location(player));
      if ((dump_messages(d, player, 0)) == 0) {
	d->connected = 2;
	return 0;
      }
    }

  } else if (!strcasecmp(command, "cd")) {
    if ((player = connect_player(user, password, d->addr)) == NOTHING) {
      queue_string(d, connect_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed connect to '%s'.",
	     d->descriptor, d->addr, user);
    } else {
      do_log(LT_CONN, 0, 0, "[%d/%s] Connected dark to %s(#%d) in %s(#%d)",
	     d->descriptor, d->addr, Name(player), player,
	     Name(Location(player)), Location(player));
      /* Set player dark here - check this */
      d->connected = 1;
      d->player = player;
      set_flag(player, player, (char *) "DARK", 0, 0, 0);
      if ((dump_messages(d, player, 0)) == 0) {
	d->connected = 2;
	return 0;
      }
    }

  } else if (!strcasecmp(command, "ch")) {
    if ((player = connect_player(user, password, d->addr)) == NOTHING) {
      queue_string(d, connect_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed connect to '%s'.",
	     d->descriptor, d->addr, user);
    } else {
      do_log(LT_CONN, 0, 0, "[%d/%s] Connected dark to %s(#%d) in %s(#%d)",
	     d->descriptor, d->addr, Name(player), player,
	     Name(Location(player)), Location(player));
      /* Set player dark here - check this */
      d->connected = 1;
      d->player = player;
      if (Can_Hide(player))
	d->hide = 1;
      if ((dump_messages(d, player, 0)) == 0) {
	d->connected = 2;
	return 0;
      }
    }

  } else if (string_prefix("create", command)) {
    if (!Site_Can_Create(d->addr)) {
      fcache_dump(d, options.register_fcache);
      do_log(LT_CONN, 0, 0, "[%d/%s] Refused create for '%s'.",
	     d->descriptor, d->addr, user);
      fflush(connlog_fp);
      return 0;
    }
    if (!options.login_allow || !options.create_allow) {
      fcache_dump(d, options.down_fcache);
      fprintf(connlog_fp,
	      "REFUSED CREATION for %s from %s on descriptor %d.\n",
	      user, d->addr, d->descriptor);
      fflush(connlog_fp);
      return 0;
    } else if (MAX_LOGINS && !under_limit) {
      fcache_dump(d, options.full_fcache);
      fprintf(connlog_fp,
	      "REFUSED CREATION for %s from %s on descriptor %d.\n",
	      user, d->addr, d->descriptor);
      fflush(connlog_fp);
      return 0;
    }
    player = create_player(user, password, d->addr);
    if (player == NOTHING) {
      queue_string(d, create_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed create for '%s' (bad name).",
	     d->descriptor, d->addr, user);
    } else if (player == AMBIGUOUS) {
      queue_string(d, create_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed create for '%s' (bad password).",
	     d->descriptor, d->addr, user);
    } else {
      do_log(LT_CONN, 0, 0, "[%d/%s] Created %s(#%d)",
	     d->descriptor, d->addr, Name(player), player);
      if ((dump_messages(d, player, 1)) == 0) {
	d->connected = 2;
	return 0;
      }
    }				/* successful player creation */

  } else if (string_prefix("register", command)) {
    if (!Site_Can_Register(d->addr)) {
      fcache_dump(d, options.register_fcache);
      do_log(LT_CONN, 0, 0, "[%d/%s] Refused registration (bad site) for '%s'.",
	     d->descriptor, d->addr, user);
      fflush(connlog_fp);
      return 0;
    }
    if (!options.create_allow) {
      fcache_dump(d, options.down_fcache);
      fprintf(connlog_fp,
	      "Refused registration (creation disabled) for %s from %s on descriptor %d.\n",
	      user, d->addr, d->descriptor);
      fflush(connlog_fp);
      return 0;
    }
    if ((player = email_register_player(user, password, d->addr)) == NOTHING) {
      queue_string(d, register_fail);
      do_log(LT_CONN, 0, 0, "[%d/%s] Failed registration for '%s'.",
	     d->descriptor, d->addr, user);
    } else {
      queue_string(d, register_success);
      do_log(LT_CONN, 0, 0, "[%d/%s] Registered %s(#%d) to %s",
	     d->descriptor, d->addr, Name(player), player, password);
    }
    /* Even when registration succeeds, they don't connect, so return 0 */
    return 0;

  } else {
    /* invalid command, just repeat login screen */
    welcome_user(d);
  }
  fflush(connlog_fp);
  return 1;
}

void
parse_connect(msg, command, user, pass)
    const char *msg;
    char *command;
    char *user;
    char *pass;
{
  char *p;

  while (*msg && isascii(*msg) && isspace(*msg))
    msg++;
  p = command;
  while (*msg && isascii(*msg) && !isspace(*msg))
    *p++ = *msg++;
  *p = '\0';
  while (*msg && isascii(*msg) && isspace(*msg))
    msg++;
  p = user;

  if (PLAYER_NAME_SPACES && *msg == '\"') {
    for (; *msg && ((*msg == '\"') || isspace(*msg)); msg++) ;
    while (*msg && (*msg != '\"')) {
      while (*msg && !isspace(*msg) && (*msg != '\"'))
	*p++ = *msg++;
      if (*msg == '\"') {
	msg++;
	while (*msg && isspace(*msg))
	  msg++;
	break;
      }
      while (*msg && isspace(*msg))
	msg++;
      if (*msg && (*msg != '\"'))
	*p++ = ' ';
    }
  } else
    while (*msg && isascii(*msg) && !isspace(*msg))
      *p++ = *msg++;

  *p = '\0';
  while (*msg && isascii(*msg) && isspace(*msg))
    msg++;
  p = pass;
  while (*msg && isascii(*msg) && !isspace(*msg))
    *p++ = *msg++;
  *p = '\0';
}

void
close_sockets()
{
  struct descriptor_data *d, *dnext;

  if (USE_RWHO)
    rwhocli_shutdown();
  for (d = descriptor_list; d; d = dnext) {
    dnext = d->next;
#ifdef WIN32
    send(d->descriptor, shutdown_message, strlen(shutdown_message), 0);
#else
    write(d->descriptor, shutdown_message, strlen(shutdown_message));
#endif
    if (shutdown(d->descriptor, 2) < 0)
      perror("shutdown");
    closesocket(d->descriptor);
  }
}
void
emergency_shutdown()
{
  close_sockets();
}

void
boot_desc(d)
    DESC *d;
{
  shutdownsock(d);
}

DESC *
player_desc(player)
    dbref player;
{
  DESC *d;
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected && (d->player == player)) {
      return d;
    }
  }
  return (DESC *) NULL;
}

DESC *
inactive_desc(player)
    dbref player;
{
  DESC *d;
  time_t now;
  now = time((time_t *) 0);
  DESC_ITER_CONN(d) {
    if ((d->player == player) && (now - d->last_time > 60)) {
      return d;
    }
  }
  return (DESC *) NULL;
}

DESC *
port_desc(port)
    int port;
{
  DESC *d;
  for (d = descriptor_list; (d); d = d->next) {
    if (d->descriptor == port) {
      return d;
    }
  }
  return (DESC *) NULL;
}

dbref
find_player_by_desc(port)
    int port;
{
  /* given a descriptor, find the matching player dbref */

  struct descriptor_data *d;
  for (d = descriptor_list; (d); d = d->next) {
    if (d->connected && (d->descriptor == port)) {
      return d->player;
    }
  }

  /* didn't find anything */
  return NOTHING;
}

Signal_t
bailout(sig)
    int sig;
{
  char tbuf1[BUFFER_LEN];

  sprintf(tbuf1, "BAILOUT: caught signal %d", sig);
  panic(tbuf1);
  _exit(7);
#ifndef VOIDSIG
  return 0;
#endif
}

static void
dump_info(call_by)
    DESC *call_by;
{
  int count = 0;
  DESC *d;
  queue_string(call_by, tprintf("### Begin INFO %s\r\n", INFO_VERSION));
  /* Count connected players */
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      if (!GoodObject(d->player))
	continue;
      if (COUNT_ALL || !Hidden(d))
	count++;
    }
  }
  queue_string(call_by, tprintf("Name: %s\r\n", options.mud_name));
  queue_string(call_by, tprintf("Uptime: %s\r", ctime(&start_time)));
  queue_string(call_by, tprintf("Connected: %d\r\n", count));
  queue_string(call_by, tprintf("Size: %d\r\n", db_top));
  queue_string(call_by, tprintf("Version: %s\r\n", SHORTVN));
  queue_string(call_by, "### End INFO\r\n");
}

void
dump_users(call_by, match, doing)
    struct descriptor_data *call_by;
    char *match;
    int doing;


  /* 0 if normal WHO, 1 if DOING */
{
  struct descriptor_data *d;
  int count = 0;
  time_t now;
  char tbuf1[BUFFER_LEN];
  char tbuf2[BUFFER_LEN];

  if (!GoodObject(call_by->player)) {
    do_log(LT_ERR, 0, 0, "Bogus caller #%d of dump_users", call_by->player);
    return;
  }
  while (*match && *match == ' ')
    match++;
  now = time((time_t *) 0);

/* If a wizard/royal types "DOING" it gives him the normal player WHO,
 * BUT flags are not shown. Wizard/royal WHO does not show @doings.
 */

  if (SUPPORT_PUEBLO && call_by->pueblo)
    queue_newwrite(call_by, "<PRE>", 5);

  if ((doing) || !call_by->player || !Priv_Who(call_by->player)) {
    if (poll_msg[0] == '\0')
      strcpy(poll_msg, "Doing");
#ifdef EXTENDED_ANSI
    if (ShowAnsi(call_by->player))
      sprintf(tbuf2, "Player Name          On For   Idle  %s%s\r\n",
	      poll_msg, ANSI_NORMAL);
    else
#endif
      sprintf(tbuf2, "Player Name          On For   Idle  %s\r\n",
	      poll_msg);
    queue_string(call_by, tbuf2);
  } else {
    queue_string(call_by,
	  "Player Name     Room #    On For  Idle  Cmds  Des  Host\r\n");
  }

  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      if (!GoodObject(d->player))
	continue;
      if (COUNT_ALL || (!Hidden(d) || (call_by->player && Priv_Who(call_by->player))))
	count++;
      if (match && !(string_prefix(Name(d->player), match)))
	continue;

      if (call_by->connected && !(doing) && call_by->player && Priv_Who(call_by->player)) {
	sprintf(tbuf1, "%-16s %5d %9s %5s  %4d  %3d  %s",
		Name(d->player),
		Location(d->player),
		time_format_1(now - d->connected_at),
		time_format_2(now - d->last_time),
		d->cmds,
		d->descriptor,
		d->addr);
	tbuf1[78] = '\0';
	if (Dark(d->player)) {
	  tbuf1[71] = '\0';
	  strcat(tbuf1, " (Dark)");
	} else if (Hidden(d)) {
	  tbuf1[71] = '\0';
	  strcat(tbuf1, " (Hide)");
	}
      } else {
	if (!Hidden(d) || (call_by->player && Priv_Who(call_by->player) && (doing))) {
	  sprintf(tbuf1, "%-16s %10s   %4s%c %s",
		  Name(d->player),
		  time_format_1(now - d->connected_at),
		  time_format_2(now - d->last_time)
		  ,(Dark(d->player) ? 'D' :
		    (Hidden(d) ? 'H' : ' '))
		  ,d->doing
	    );
	}
      }

      if (!Hidden(d) || (call_by->player && Priv_Who(call_by->player))) {
	queue_string(call_by, tbuf1);
	if (SUPPORT_PUEBLO && call_by->pueblo)
	  queue_newwrite(call_by, "\n", 1);
	else
	  queue_newwrite(call_by, "\r\n", 2);
      }
    }
  }
  if (SUPPORT_PUEBLO && call_by->pueblo)
    queue_newwrite(call_by, "</PRE>\n", 7);
  switch (count) {
  case 0:
    strcpy(tbuf1, "There are no players connected.\r\n");
    break;
  case 1:
    strcpy(tbuf1, "There is 1 player connected.\r\n");
    break;
  default:
    sprintf(tbuf1, "There are %d players connected.\r\n", count);
    break;
  }
  queue_string(call_by, tbuf1);
}

const char *
time_format_1(dt)
    long int dt;
{
  register struct tm *delta;
  time_t holder;		/* A hack for 64bit SGI */
  static char buf[64];
  if (dt < 0)
    dt = 0;
  holder = (time_t) dt;
  delta = gmtime(&holder);
  if (delta->tm_yday > 0) {
    sprintf(buf, "%dd %02d:%02d",
/*    sprintf(buf, "%d:%02d:%02d", */
	    delta->tm_yday, delta->tm_hour, delta->tm_min);
  } else {
    sprintf(buf, "%02d:%02d",
	    delta->tm_hour, delta->tm_min);
  }
  return buf;
}

const char *
time_format_2(dt)
    long int dt;
{
  register struct tm *delta;
  static char buf[64];
  if (dt < 0)
    dt = 0;

  delta = gmtime((time_t *) & dt);
  if (delta->tm_yday > 0) {
    sprintf(buf, "%dd", delta->tm_yday);
  } else if (delta->tm_hour > 0) {
    sprintf(buf, "%dh", delta->tm_hour);
  } else if (delta->tm_min > 0) {
    sprintf(buf, "%dm", delta->tm_min);
  } else {
    sprintf(buf, "%ds", delta->tm_sec);
  }
  return buf;
}

void
announce_connect(player)
    dbref player;
{
  dbref loc;
  ATTR *temp;
  char tbuf1[BUFFER_LEN];
  dbref zone;
  dbref obj;
  char *s;
  DESC *d;
  int num = 0;
  int is_hidden;
  int j;

#ifdef CHAT_SYSTEM
  CHAN *c;
  CHANUSER *u;
#endif

  Toggles(player) |= PLAYER_CONNECT;

  is_hidden = Can_Hide(player) && (Flags(player) & DARK);

  /* send out RWHO stuff. We're going to use tbuf1 in a moment again */
  if (USE_RWHO) {
    sprintf(tbuf1, "%d@%s", player, MUDNAME);
    rwhocli_userlogin(tbuf1, Name(player), time((time_t *) 0));
  }
  /* check to see if this is a reconnect and also set DARK status */
  DESC_ITER_CONN(d) {
    if (d->player == player) {
      num++;
      if (is_hidden)
	d->hide = 1;
    }
  }

  sprintf(tbuf1, "%s has %s%sconnected.", Name(player),
	  Dark(player) ? "DARK-" : "", (num > 1) ? "re" : "");

  /* send out messages */
  if (Suspect(player))
    flag_broadcast(WIZARD, 0, "Broadcast: Suspect %s", tbuf1);

  if (Dark(player)) {
#ifdef ROYALTY_FLAG
    flag_broadcast(WIZARD | ROYALTY, PLAYER_MONITOR, "GAME: %s", tbuf1);
#else
    flag_broadcast(WIZARD, PLAYER_MONITOR, "GAME: %s", tbuf1);
#endif
  } else
    flag_broadcast(0, PLAYER_MONITOR, "GAME: %s", tbuf1);

  /* tell players on a channel when someone connects */
#ifdef CHAT_SYSTEM
  for (c = channels; c; c = c->next) {
    u = onchannel(player, c);
    if (u &&!Channel_Quiet(c) && (Channel_Admin(c) || Channel_Wizard(c)
			      || (!Chanuser_Hide(u) && !Dark(player)))) {
      channel_broadcast(c, 1, "<%s> %s", ChanName(c), tbuf1);
    }
  }
#endif				/* CHAT_SYSTEM */

  loc = Location(player);
  if (!GoodObject(loc)) {
    notify(player, "You are nowhere!");
    return;
  }
  orator = player;

  raw_notify(player, asterisk_line);
  if (cf_motd_msg && *cf_motd_msg)
    raw_notify(player, cf_motd_msg);
  raw_notify(player, " ");
  if (Hasprivs(player) && cf_wizmotd_msg && *cf_wizmotd_msg)
    raw_notify(player, cf_wizmotd_msg);
  raw_notify(player, asterisk_line);

  notify_except(db[player].contents, player, tbuf1);
  /* added to allow player's inventory to hear a player connect */

  if (!Dark(player))
    notify_except(db[loc].contents, player, tbuf1);

  /* clear the environment for possible actions */
  for (j = 0; j < 10; j++) {
    wnxt[j] = NULL;
    rnxt[j] = NULL;
  }

  /* do the person's personal connect action */
  temp = atr_get(player, "ACONNECT");
  if (temp) {
    s = safe_uncompress(temp->value);
    parse_que(player, s, player);
    free((Malloc_t) s);
  }
  if (ROOM_CONNECTS) {
    /* Do the room the player connected into */
    if (Typeof(loc) == TYPE_ROOM) {
      temp = atr_get(loc, "ACONNECT");
      if (temp) {
	s = safe_uncompress(temp->value);
	parse_que(loc, s, player);
	free((Malloc_t) s);
      }
    }
  }
  if (GLOBAL_CONNECTS) {
    /* do the zone of the player's location's possible aconnect */
    if ((zone = Zone(loc)) != NOTHING) {
      switch (Typeof(zone)) {
      case TYPE_THING:
	temp = atr_get(zone, "ACONNECT");
	if (temp) {
	  s = safe_uncompress(temp->value);
	  parse_que(zone, s, player);
	  free((Malloc_t) s);
	}
	break;
      case TYPE_ROOM:
	/* check every object in the room for a connect action */
	DOLIST(obj, db[zone].contents) {
	  temp = atr_get(obj, "ACONNECT");
	  if (temp) {
	    s = safe_uncompress(temp->value);
	    parse_que(obj, s, player);
	    free((Malloc_t) s);
	  }
	}
	break;
      default:
	do_log(LT_ERR, 0, 0, "Invalid zone #%d for %s(#%d) has bad type %d",
	       zone, Name(player), player, Typeof(zone));
      }
    }
    /* now try the master room */
    if (DO_GLOBALS) {
      DOLIST(obj, db[MASTER_ROOM].contents) {
	temp = atr_get(obj, "ACONNECT");
	if (temp) {
	  s = safe_uncompress(temp->value);
	  parse_que(obj, s, player);
	  free((Malloc_t) s);
	}
      }
    }
  }
}

void
announce_disconnect(player)
    dbref player;
{
  dbref loc;
  int num;
  ATTR *temp;
  struct descriptor_data *d;
  char tbuf1[BUFFER_LEN];
  dbref zone, obj;
  char *s;
  char *p;
  time_t tt;
  int j;

#ifdef CHAT_SYSTEM
  CHAN *c;
  CHANUSER *u;
#endif

  tt = time((time_t *) 0);
  p = ctime(&tt);
  p[strlen(p) - 1] = 0;

  loc = Location(player);
  if (!GoodObject(loc))
    return;

  orator = player;

  for (num = 0, d = descriptor_list; d; d = d->next)
    if (d->connected && (d->player == player))
      num++;
  if (num < 2) {

    if (USE_RWHO) {
      sprintf(tbuf1, "%d@%s", player, MUDNAME);
      rwhocli_userlogout(tbuf1);
    }
    sprintf(tbuf1, "%s has disconnected.", Name(player));

    if (!Dark(player))
      notify_except(db[loc].contents, player, tbuf1);
    /* notify contents */
    notify_except(db[player].contents, player, tbuf1);

    /* clear the environment for possible actions */
    for (j = 0; j < 10; j++) {
      wnxt[j] = NULL;
      rnxt[j] = NULL;
    }

    temp = atr_get(player, "ADISCONNECT");
    if (temp) {
      s = safe_uncompress(temp->value);
      parse_que(player, s, player);
      free((Malloc_t) s);
    }
    if (ROOM_CONNECTS)
      if (Typeof(loc) == TYPE_ROOM) {
	temp = atr_get(loc, "ADISCONNECT");
	if (temp) {
	  s = safe_uncompress(temp->value);
	  parse_que(loc, s, player);
	  free((Malloc_t) s);
	}
      }
    if (GLOBAL_CONNECTS) {
      /* do the zone of the player's location's possible adisconnect */
      if ((zone = Zone(loc)) != NOTHING) {
	switch (Typeof(zone)) {
	case TYPE_THING:
	  temp = atr_get(zone, "ADISCONNECT");
	  if (temp) {
	    s = safe_uncompress(temp->value);
	    parse_que(zone, s, player);
	    free((Malloc_t) s);
	  }
	  break;
	case TYPE_ROOM:
	  /* check every object in the room for a connect action */
	  DOLIST(obj, db[zone].contents) {
	    temp = atr_get(obj, "ADISCONNECT");
	    if (temp) {
	      s = safe_uncompress(temp->value);
	      parse_que(obj, s, player);
	      free((Malloc_t) s);
	    }
	  }
	  break;
	default:
	  do_log(LT_ERR, 0, 0, "Invalid zone #%d for %s(#%d) has bad type %d",
		 zone, Name(player), player, Typeof(zone));
	}
      }
      /* now try the master room */
      if (DO_GLOBALS) {
	DOLIST(obj, db[MASTER_ROOM].contents) {
	  temp = atr_get(obj, "ADISCONNECT");
	  if (temp) {
	    s = safe_uncompress(temp->value);
	    parse_que(obj, s, player);
	    free((Malloc_t) s);
	  }
	}
      }
    }
    Toggles(player) &= ~PLAYER_CONNECT;

    sprintf(tbuf1, "%s has disconnected.", Name(player));
  } else {
    /* note: when you partially disconnect, ADISCONNECTS are not executed */
    sprintf(tbuf1, "%s has partially disconnected.", Name(player));

    if (!Dark(player))
      notify_except(db[loc].contents, player, tbuf1);
    /* notify contents */
    notify_except(db[player].contents, player, tbuf1);
  }
  /* now print messages */
  if (Suspect(player))
    flag_broadcast(WIZARD, 0, "Broadcast: Suspect %s", tbuf1);

  if (Dark(player)) {
    sprintf(tbuf1, "%s has DARK-disconnected.", Name(player));
#ifdef ROYALTY_FLAG
    flag_broadcast(WIZARD | ROYALTY, PLAYER_MONITOR, "GAME: %s", tbuf1);
#else
    flag_broadcast(WIZARD, PLAYER_MONITOR, "GAME: %s", tbuf1);
#endif
  } else
    flag_broadcast(0, PLAYER_MONITOR, "GAME: %s", tbuf1);

  /* tell players on channel that someone's left */
#ifdef CHAT_SYSTEM
  for (c = channels; c; c = c->next) {
    u = onchannel(player, c);
    if (u) {
      if ((!Dark(player) || Channel_Admin(c) || Channel_Wizard(c)) &&
	  !Chanuser_Hide(u) && !Channel_Quiet(c)) {
	channel_broadcast(c, 1, "<%s> %s", ChanName(c), tbuf1);
      }
      /* Ungag them from the channel */
      do_chan_user_flags(player, ChanName(c), "no", 2, 1);
    }
  }
#endif				/* CHAT_SYSTEM */
}

void
do_motd(player, key, message)
    dbref player;
    int key;
    const char *message;
{

  if (!Wizard(player) && (key != 3)) {
    notify(player,
	   "You may get 15 minutes of fame and glory in life, but not right now.");
    return;
  }
  switch (key) {
  case 1:
    strcpy(cf_motd_msg, message);
    notify(player, "Motd set.");
    break;
  case 2:
    strcpy(cf_wizmotd_msg, message);
    notify(player, "Wizard motd set.");
    break;
  case 4:
    strcpy(cf_downmotd_msg, message);
    notify(player, "Down motd set.");
    break;
  case 5:
    strcpy(cf_fullmotd_msg, message);
    notify(player, "Full motd set.");
    break;
  default:
    notify(player, tprintf("MOTD: %s", cf_motd_msg));
    if (Hasprivs(player)) {
      notify(player, tprintf("Wiz MOTD: %s", cf_wizmotd_msg));
      notify(player, tprintf("Down MOTD: %s", cf_downmotd_msg));
      notify(player, tprintf("Full MOTD: %s", cf_fullmotd_msg));
    }
  }
}

void
do_doing(player, message)
    dbref player;
    const char *message;
{
  char buf[MAX_COMMAND_LEN];
  struct descriptor_data *d;
  int i;

  if (!Connected(player)) {
    /* non-connected things have no need for a doing */
    notify(player, "Why would you want to do that?");
    return;
  }
  strncpy(buf, message, DOING_LEN - 1);

  /* now smash undesirable characters and truncate */
  for (i = 0; i < DOING_LEN; i++) {
    if ((buf[i] == '\r') || (buf[i] == '\n') || (buf[i] == 27) ||
	(buf[i] == '\t') || (buf[i] == BEEP_CHAR))
      buf[i] = ' ';
  }
  buf[DOING_LEN - 1] = '\0';

  /* set it */
  for (d = descriptor_list; d; d = d->next)
    if (d->connected && (d->player == player))
      strcpy(d->doing, buf);
  if (strlen(message) >= DOING_LEN) {
    notify(player,
	   tprintf("Doing set. %d characters lost.", strlen(message) - (DOING_LEN - 1)));
  } else
    notify(player, "Doing set.");
}

/* this sets the message which replaces "Doing" */
void
do_poll(player, message)
    dbref player;
    const char *message;
{

  if (!Change_Poll(player)) {
    notify(player, "Who do you think you are, Gallup?");
    return;
  }
  strncpy(poll_msg, message, DOING_LEN - 1);
  if (strlen(message) >= DOING_LEN) {
    poll_msg[DOING_LEN - 1] = 0;
    notify(player,
	   tprintf("Poll set. %d characters lost.", strlen(message) - (DOING_LEN - 1)));
  } else
    notify(player, "Poll set.");
  do_log(LT_WIZ, player, NOTHING, "Poll Set to '%s'.", poll_msg);
  fflush(wizlog_fp);
}

void
rwho_update()
{
  struct descriptor_data *d;
  char tbuf1[BUFFER_LEN];

  rwhocli_pingalive();
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected && !Hidden(d)) {
      sprintf(tbuf1, "%d@%s", d->player, MUDNAME);
      rwhocli_userlogin(tbuf1, Name(d->player), d->connected_at);
    }
  }
}

const char *
hostname_convert(nums)
    struct in_addr nums;
{
/* given an address, convert it to either IP numbers or a hostname */

  struct hostent *he;

  if (options.use_dns) {
    he = gethostbyaddr((char *) &nums.s_addr, sizeof(nums.s_addr), AF_INET);
    if (he == NULL)
      return (inet_ntoa(nums));	/* IP numbers */
    else
      return (he->h_name);	/* hostname */
  } else
    return (inet_ntoa(nums));
}

dbref
short_page(match)
    const char *match;
{
  /* attempts to match to the partial name of a connected player */

  struct descriptor_data *d;
  dbref who1 = NOTHING;
  int count = 0;

  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      if (match && !string_prefix(Name(d->player), match))
	continue;
      if (!strcasecmp(Name(d->player), match)) {
	count = 1;
	who1 = d->player;
	break;
      }
      if (who1 == NOTHING || d->player != who1) {
	who1 = d->player;
	count++;
      }
    }
  }

  if (count > 1)
    return AMBIGUOUS;
  else if (count == 0)
    return NOTHING;

  return who1;
}


/* LWHO() function - really belongs in eval.c but needs stuff declared here */

/* ARGSUSED */
FUNCTION(fun_lwho)
{
  DESC *d;
  int first;
  int powered = (*called_as == 'L');

  first = 1;
  DESC_ITER_CONN(d) {
    if (!Hidden(d) || (powered && Priv_Who(executor))) {
      if (first)
	first = 0;
      else
	safe_chr(' ', buff, bp);
      safe_str(unparse_dbref(d->player), buff, bp);
    }
  }
}

/* ARGSUSED */
FUNCTION(fun_doing)
{
  /* Gets a player's @doing */
  DESC *d;
  dbref target;

  target = lookup_player(args[0]);
  if (target == NOTHING) {
    target = match_result(executor, args[0], TYPE_PLAYER,
			  MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
  }
  if ((target == NOTHING) || !Connected(target)) {
    safe_str("#-1", buff, bp);
    return;
  }
  /* else walk the descriptor list looking for a match */
  DESC_ITER_CONN(d) {
    if (d->player == target) {
      if (!Hidden(d) || Priv_Who(executor))
	safe_str(d->doing, buff, bp);
      else
	safe_str("#-1", buff, bp);
      return;
    }
  }

  /* if we hit this point we are in trouble */
  safe_str("#-1", buff, bp);
  do_log(LT_ERR, 0, 0,
	 "Whoa. doing() can't find player #%d on call by #%d\n",
	 target, executor);
}

FUNCTION(fun_poll)
{
  /* Gets the current poll */
  if (poll_msg[0] == '\0')
    strcpy(poll_msg, "Doing");

  safe_str(poll_msg, buff, bp);
}

FUNCTION(fun_pueblo)
{
  /* Return the status of the pueblo flag on the least idle descriptor we
   * find that matches the player's dbref. 
   */
  DESC *d;
  dbref target;
  DESC *match;

  target = lookup_player(args[0]);
  if (target == NOTHING) {
    target = match_result(executor, args[0], TYPE_PLAYER,
			  MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
  }
  /* non-connected players return error #-1 */
  if ((target == NOTHING) || !Connected(target)) {
    safe_str("#-1 NOT CONNECTED", buff, bp);
    return;
  }
  /* else walk the descriptor list looking for a match */
  match = NULL;
  DESC_ITER_CONN(d) {
    if ((d->player == target) &&
	(!Hidden(d) || Priv_Who(executor)) &&
	(!match || (d->last_time > match->last_time)))
      match = d;
  }
  if (match)
    safe_str(unparse_integer(match->pueblo), buff, bp);
  else
    safe_str("#-1 NOT CONNECTED", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_idlesecs)
{
  /* returns the number of seconds a player has been idle, using
   * their least idle connection
   */

  time_t now;
  DESC *d;
  DESC *match;
  dbref target;

  now = time((time_t *) 0);

  target = lookup_player(args[0]);
  if (target == NOTHING) {
    target = match_result(executor, args[0], TYPE_PLAYER,
			  MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
  }
  /* non-connected players return error -1 */
  if ((target == NOTHING) || !Connected(target)) {
    safe_str("-1", buff, bp);
    return;
  }
  /* else walk the descriptor list looking for a match */
  match = NULL;
  DESC_ITER_CONN(d) {
    if ((d->player == target) &&
	(!Hidden(d) || Priv_Who(executor)) &&
	(!match || (d->last_time > match->last_time)))
      match = d;
  }

  if (match)
    safe_str(unparse_integer(now - match->last_time), buff, bp);
  else
    safe_str("-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_conn)
{
  /* returns the number of seconds a player has been connected, using
   * their longest-connected descriptor
   */

  time_t now;
  DESC *d;
  DESC *match;
  dbref target;

  now = time((time_t *) 0);

  target = lookup_player(args[0]);
  if (target == NOTHING) {
    target = match_result(executor, args[0], TYPE_PLAYER,
			  MAT_ABSOLUTE | MAT_PLAYER);
  }
  /* non-connected players and dark wizards return error, -1 */
  if ((target == NOTHING) || !Connected(target)) {
    safe_str("-1", buff, bp);
    return;
  }
  /* else walk the descriptor list looking for a match */
  match = NULL;
  DESC_ITER_CONN(d) {
    if ((d->player == target) &&
	(!Hidden(d) || Priv_Who(executor)) &&
	(!match || (d->connected_at < match->connected_at)))
      match = d;
  }

  if (match)
    safe_str(unparse_integer(now - match->connected_at), buff, bp);
  else
    safe_str("-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_ports)
{
  /* returns a list of the network descriptors that a player is 
   * connected to (wizard-only)
   */

  dbref target;
  DESC *d;
  int first;

  if (!Hasprivs(executor)) {
    notify(executor, "Permission denied.");
    return;
  }
  target = lookup_player(args[0]);
  if (target == NOTHING) {
    target = match_result(executor, args[0], TYPE_PLAYER,
			  MAT_ABSOLUTE | MAT_PLAYER);
  }
  if ((target == NOTHING) || !Connected(target)) {
    return;
  }
  /* Walk descriptor chain. Don't worry about buffer length limits. */
  first = 1;
  DESC_ITER_CONN(d) {
    if (d->player == target) {
      if (first)
	first = 0;
      else
	safe_chr(' ', buff, bp);
      safe_str(unparse_integer(d->descriptor), buff, bp);
    }
  }
}


void
hide_player(player, hide)
    dbref player;
    int hide;

  /* hide player? */
{
  DESC *d;
  char buf[BUFFER_LEN];

  if (!Connected(player))
    return;

  if (!Can_Hide(player)) {
    notify(player, "Permission denied.");
    return;
  }
  /* change status on WHO */

  if (Can_Hide(player)) {
    DESC_ITER_CONN(d) {
      if (d->player == player)
	d->hide = hide;
    }
  }
  if (hide)
    notify(player, "You no longer appear on the WHO list.");
  else
    notify(player, "You now appear on the WHO list.");

  /* change status on RWHO if necessary */
  if (USE_RWHO) {
    sprintf(buf, "%d@%s", player, MUDNAME);
    if (hide)
      rwhocli_userlogout(buf);
    else
      rwhocli_userlogin(buf, Name(player), time((time_t *) NULL));
  }
}

void
inactivity_check()
{
  DESC *d, *nextd;
  register struct tm *idle;
  time_t now;
  int check, hrs, mns;
  char buf[BUFFER_LEN];

  if (!INACTIVITY_LIMIT)
    return;

  check = hrs = mns = 0;
  now = time((time_t *) 0);

  for (mns = INACTIVITY_LIMIT; mns > 60; mns -= 60, hrs++) ;

  for (d = descriptor_list; d; d = nextd) {
    nextd = d->next;
    check = (now - d->last_time);
    idle = gmtime((time_t *) & check);

    if ((idle->tm_hour > hrs) ||
	((idle->tm_hour == hrs) && (idle->tm_min >= mns))) {

      if (!d->connected)
	shutdownsock(d);

      else if (!Can_Idle(d->player)) {

	notify(d->player, "\n*** Inactivity timeout ***\n");
	do_log(LT_CONN, 0, 0, "[%d/%s] Logout by %s(#%d) <Inactivity Timeout>",
	       d->descriptor, d->addr, Name(d->player), d->player);
	fflush(connlog_fp);
	boot_desc(d);

      } else if (Unfind(d->player)) {

	if ((Can_Hide(d->player)) && (!Hidden(d))) {
	  notify(d->player,
	    "\n*** Inactivity limit reached. You are now HIDDEN. ***\n");
	  d->hide = 1;
	  if (USE_RWHO) {
	    sprintf(buf, "%d@%s", d->player, MUDNAME);
	    rwhocli_userlogout(buf);
	  }
	}
      }
    }
  }
}


#ifdef CHAT_SYSTEM


dbref
na_channel(current, data)
    dbref current;
    void *data;
{
  struct na_cpass *nac = data;
  CHANUSER *u, *nu;
  int cont;

  nu = nac->u;
  do {
    u = nu;
    if (!u)
      return NOTHING;
    current = CUdbref(u);
    nu = u->next;
    cont = (!GoodObject(current) ||
	    (nac->checkquiet && Chanuser_Quiet(u)) ||
	    Chanuser_Gag(u) ||
	     ((Typeof(current) == TYPE_PLAYER) && !Connected(current)));
  } while (cont);
  nac->u = nu;
  return current;
}

#ifdef I_STDARG
void
channel_broadcast(CHAN *channel, int flags, const char *fmt,...)
#else
void
channel_broadcast(channel, flags, va_alist)
    CHAN *channel;
    int flags;			/* 0x1 = checkquiet, 0x2 = nospoof */
    va_dcl
#endif
{
  va_list args;
  char tbuf1[BUFFER_LEN];
#ifndef I_STDARG
  char *fmt;
#endif
  struct na_cpass nac;

  /* Make sure we can write to the channel before doing so */
  if (Channel_Disabled(channel))
    return;

#ifndef I_STDARG
  va_start(args);
  fmt = va_arg(args, char *);
#else
  va_start(args, fmt);
#endif

  (void) vsprintf(tbuf1, fmt, args);
  va_end(args);

  nac.u = ChanUsers(channel);
  nac.checkquiet = (flags & 0x1) ? 1 : 0;
  notify_anything(orator, na_channel, &nac, (flags & 0x2) ? ns_esnotify : NULL, 0, tbuf1);
}

void
do_channel_who(player, chan)
    dbref player;
    CHAN *chan;
{
  DESC *d;
  char tbuf1[BUFFER_LEN];
  char *bp;
  CHANUSER *u;

  bp = tbuf1;
  DESC_ITER_CONN(d) {
    u = onchannel(d->player, chan);
    if (u &&(!Chanuser_Hide(u) || Priv_Who(player))) {
      if (bp != tbuf1)
	safe_str(", ", tbuf1, &bp);
      safe_str(Name(d->player), tbuf1, &bp);
      if (Chanuser_Gag(u))
	 safe_str(" (gagging)", tbuf1, &bp);
    }
  }
  *bp = '\0';

  if (!*tbuf1)
    notify(player, "There are no connected players on that channel.");
  else {
    notify(player, "Connected players on that channel are:");
    notify(player, tbuf1);
  }
}

/* ARGSUSED */
FUNCTION(fun_cwho)
{
  DESC *d;
  int first = 1;
  CHAN *chan = NULL;
  CHANUSER *u;
  int i;

  i = find_channel(args[0], &chan);
  switch (i) {
  case CMATCH_NONE:
    notify(executor, "No such channel.");
    return;
  case CMATCH_AMBIG:
    notify(executor, "I can't tell which channel you mean.");
    return;
  }

  /* Feh. We need to do some sort of privilege checking, so that
   * if mortals can't do '@channel/who wizard', they can't do 
   * 'think cwho(wizard)' either. The first approach that comes to
   * mind is the following:
   * if (!ChannelPermit(privs,chan)) ...
   * Unfortunately, we also want objects to be able to check cwho()
   * on channels.
   * So, we check the owner, instead, because most uses of cwho()
   * are either in the Master Room owned by a wizard, or on people's
   * quicktypers.
   */

  if (!Chan_Can_See(chan, Owner(executor)) &&
      !Chan_Can_See(chan, executor)) {
    safe_str("#-1 NO PERMISSIONS FOR CHANNEL", buff, bp);
    return;
  }
  *bp = buff;

  DESC_ITER_CONN(d) {
    if ((u = onchannel(d->player, chan)) &&
	(!Chanuser_Hide(u) || Priv_Who(executor))) {
      if (first)
	first = 0;
      else
	safe_chr(' ', buff, bp);
      safe_str(unparse_dbref(d->player), buff, bp);
    }
  }
}

#endif				/* CHAT_SYSTEM */

int
hidden(player)
    dbref player;
{
  DESC *d;

  DESC_ITER_CONN(d) {
    if (d->player == player) {
      if (Hidden(d))
	return 1;
      else
	return 0;
    }
  }
  return 0;
}


#ifdef USE_MAILER
/* Return the mailp of the player closest in db# to player,
 * or NULL if there's nobody on-line
 */
struct mail *
desc_mail(player)
    dbref player;
{
  DESC *d;
  int i;
  int diff = db_top;
  static struct mail *mp;

  mp = NULL;
  DESC_ITER_CONN(d) {
    i = abs(d->player - player);
    if (i == 0)
      return d->mailp;
    if ((i < diff) && d->mailp) {
      diff = i;
      mp = d->mailp;
    }
  }
  return mp;
}

void
desc_mail_set(player, mp)
    dbref player;
    struct mail *mp;
{
  DESC *d;
  DESC_ITER_CONN(d) {
    if (d->player == player)
      d->mailp = mp;
  }
}

#endif				/* USE_MAILER */



#ifdef SUN_OS
/* SunOS's implementation of stdio breaks when you get a file descriptor
 * greater than 128. Brain damage, brain damage, brain damage!
 *
 * Our objective, therefore, is not to fix stdio, but to work around it,
 * so that performance degrades semi-gracefully when you are using a lot
 * of file descriptors.
 * Therefore, we'll save a file descriptor when we start up that is less
 * than 128, so that if we get a file descriptor that is >= 128, we can
 * use our own saved file descriptor instead. This is only one level of
 * defense; if you have more than 128 fd's in use, and you try two fopen's
 * before doing an fclose(), the second will fail.
 */

FILE *
fopen(file, mode)
    const char *file;
    const char *mode;
{
/*  FILE *f; */
  int fd, rw, oflags = 0;
/*  char tbchar; */

  rw = (mode[1] == '+') || (mode[1] && (mode[2] == '+'));

  switch (*mode) {
  case 'a':
    oflags = O_CREAT | (rw ? O_RDWR : O_WRONLY);
    break;
  case 'r':
    oflags = rw ? O_RDWR : O_RDONLY;
    break;
  case 'w':
    oflags = O_TRUNC | O_CREAT | (rw ? O_RDWR : O_WRONLY);
    break;
  default:
    return (NULL);
  }
/* SunOS fopen() doesn't use the 't' or 'b' flags. */


  fd = open(file, oflags, 0666);
  if (fd < 0)
    return NULL;

  /* My addition, to cope with SunOS brain damage! */
  if (fd >= 128) {
    close(fd);
    if ((extrafd < 128) && (extrafd >= 0)) {
      close(extrafd);
      fd = open(file, oflags, 0666);
      extrafd = -1;
    } else {
      return NULL;
    }
  }
  /* End addition. */

  return fdopen(fd, mode);
}


#undef fclose(x)
int
f_close(stream)
    FILE *stream;
{
  int fd = fileno(stream);
  /* if extrafd is bad, and the fd we're closing is good, recycle the
   * fd into extrafd.
   */
  fclose(stream);
  if (((extrafd < 0)) &&
      (fd >= 0) && (fd < 128)) {
    extrafd = open("/dev/null", O_RDWR);
    if (extrafd >= 128) {
      /* To our surprise, we didn't get a usable fd. */
      close(extrafd);
      extrafd = -1;
    }
  }
  return 0;
}

#define fclose(x) f_close(x)

#endif				/* SUN_OS */

#ifdef WIN32
static int
how_many_fds()
{
  return 60;
}
#else				/* WIN32 */
static int
how_many_fds()
{
  /* Determine how many open file descriptors we're allowed
   * In order, we'll try:
   * 0. OPEN_MAX constant - POSIX.1 limits.h
   * 1. sysconf(_SC_OPEN_MAX) - POSIX.1
   * 2. getdtablesize - BSD
   * 3. NOFILE - in some sys/param.h
   * 4. _NFILE - in some stdio.h
   */
#ifdef OPEN_MAX
  static int open_max = OPEN_MAX;
#else
  static int open_max = 0;
#endif

  if (open_max)
    return open_max;

#ifndef WIN32
#ifdef HAS_SYSCONF
  errno = 0;
  if ((open_max = sysconf(_SC_OPEN_MAX)) < 0) {
    if (errno == 0)		/* Value was indeterminate */
      open_max = 0;
  }
  if (open_max)
    return open_max;
#endif
#endif
  /* Caching getdtablesize is dangerous, since it's affected by
   * getrlimit, so we don't.
   */
  open_max = 0;
  return getdtablesize();
}
#endif				/* WIN32 */

/*
 * ---------------------------------------------------------------------------
 * * dump_reboot_db: Dumps descriptor_list to REBOOTFILE
 */
void
dump_reboot_db()
{
  FILE *f;
  DESC *d;
  extern int sock;
  extern int maxd;

  f = fopen(REBOOTFILE, "w");

  /* This shouldn't happen */
  if (!f) {
    flag_broadcast(0, 0, "GAME: Error writing reboot database!");
    exit(0);
  }
  putref(f, sock);
  putref(f, maxd);

  /* First, iterate through all descriptors to get to the end 
   * we do this so the descriptor_list isn't reversed on reboot 
   */
  for (d = descriptor_list; d && d->next; d = d->next) ;

  /* Second, we iterate backwards from the end of descriptor_list
   * which is now in the d variable.
   */
  for (; d != NULL; d = d->prev) {

    putref(f, d->descriptor);
    putref(f, d->connected_at);
    putref(f, d->hide);
    putref(f, d->cmds);
    if (GoodObject(d->player))
      putref(f, d->player);
    else
      putref(f, -1);
    putref(f, d->last_time);
    if (d->output_prefix)
      putstring(f, d->output_prefix);
    else
      putstring(f, "__NONE__");
    if (d->output_suffix)
      putstring(f, d->output_suffix);
    else
      putstring(f, "__NONE__");
    putstring(f, d->addr);
    putstring(f, d->doing);
    putref(f, d->pueblo);
  }				/* for loop */

  putref(f, 0);
  fclose(f);
}

void
load_reboot_db()
{
  FILE *f;
  DESC *d = NULL;
  extern int restarting;
  extern int sock;
  extern int maxd;
  int val;
  char *temp;

  f = fopen(REBOOTFILE, "r");

  if (!f) {
    restarting = 0;
    return;
  }
  restarting = 1;

  sock = getref(f);
  maxd = getref(f);

  while ((val = getref(f)) != 0) {
    ndescriptors++;
    d = (DESC *) mush_malloc(sizeof(DESC), "restart.descriptor");
    d->descriptor = val;
    d->connected_at = getref(f);
    d->hide = getref(f);
    d->cmds = getref(f);
    d->player = getref(f);
    d->last_time = getref(f);
    d->connected = GoodObject(d->player) ? 1 : 0;
    temp = (char *) getstring_noalloc(f);

    if (!strcmp(temp, "__NONE__"))
      d->output_prefix = NULL;
    else
      set_userstring(&d->output_prefix, temp);

    temp = (char *) getstring_noalloc(f);
    if (!strcmp(temp, "__NONE__"))
      d->output_suffix = NULL;
    else
      set_userstring(&d->output_suffix, temp);

    strcpy(d->addr, getstring_noalloc(f));
    strcpy(d->doing, getstring_noalloc(f));

    d->pueblo = getref(f);

    d->output_size = 0;
    d->output.head = 0;
    d->output.tail = &d->output.head;
    d->input.head = 0;
    d->input.tail = &d->input.head;
    d->raw_input = NULL;
    d->raw_input_at = NULL;
    d->quota = options.starting_quota;
#ifdef USE_MAILER
    d->mailp = NULL;
#endif

    if (descriptor_list)
      descriptor_list->prev = d;
    d->next = descriptor_list;
    d->prev = NULL;
    descriptor_list = d;

    if (d->connected && d->player && GoodObject(d->player) &&
	Typeof(d->player) == TYPE_PLAYER)
      Toggles(d->player) |= PLAYER_CONNECT;
    else if ((!d->player || !GoodObject(d->player)) && d->connected) {
      d->connected = 0;
      d->player = 0;
    }
  }				/* while loop */

#ifdef USE_MAILER
  DESC_ITER_CONN(d) {
    d->mailp = find_exact_starting_point(d->player);
  }
#endif

  fclose(f);
  remove(REBOOTFILE);
  flag_broadcast(0, 0, "GAME: Reboot finished.");
}