muse1.7b4/
muse1.7b4/config/
muse1.7b4/doc/
muse1.7b4/run/
muse1.7b4/run/db/
muse1.7b4/src/
muse1.7b4/src/db/
muse1.7b4/src/files/
muse1.7b4/src/io/
muse1.7b4/src/prog/
muse1.7b4/src/util/
/* bsd.c */
/* $Id: bsd.c,v 1.35 1993/12/19 17:59:49 nils Exp $ */

#include "config.h"
#include "externs.h"

time_t now;

#if defined(SYSV) || defined(HPUX) || defined(SYSV_MAYBE)
#   define  mklinebuf(fp)	setvbuf(fp, NULL, _IOLBF, 0)
#else
#   define  mklinebuf(fp)	setlinebuf(fp)
#endif

#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <sys/signal.h>
#include "interface.h"
#include <netdb.h>
#include <ctype.h>
/* #include <netinet/in.h> ? */
/* #include <sys/socket.h> */

extern int errno;
extern int reserved;

#define  DEF_MODE	0644
static char *def_db_in = DEF_DB_IN;
static char *def_db_out = DEF_DB_OUT;
static char *def_logfile = DEF_LOGFILE;
static int tinyport = TINYPORT;

int	shutdown_flag = 0;
int     exit_status = 136;	/* we will change this when we set
				 * shutdown_flag to 1... */
int	sig_caught = 0;

time_t  muse_up_time;

static char *connect_fail = "Either that player does not exist, or has a different password.\n";
#ifndef WCREAT
static char *create_fail = "Either there is already a player with that name, or that name is illegal.\n";
#endif
static char *flushed_message = "<Output Flushed>\n";
static char *shutdown_message = "Going down - Bye\n";

static char *get_password = "Please enter password:\n\373\001";
static char *got_password = "\374\001";

struct descriptor_data *descriptor_list = 0;

static int sock;
int ndescriptors = 0;
char ccom[1024];
dbref cplr;

int guest_free[NUMBER_GUESTS];

static void check_connect P((struct descriptor_data *, char *));
static void parse_connect  P((char *, char *, char *, char *));
static void set_userstring  P((char **, char *));
static int do_command  P((struct descriptor_data *, char *));
static char *strsave  P((char *));
static int process_input P((struct descriptor_data *));

#define MALLOC(result, type, number) do {			\
								  if (!((result) = (type *) malloc ((number) * sizeof (type))))	\
								    panic("Out of memory");				\
								    } while (0)

#define FREE(x) (free((void *) x))

void main(argc, argv)
     int argc;
     char *argv[];
{
  init_args(argc, argv);
  init_io();
  printf("--------------------------------\n");
  printf("MUSE online (pid=%d)\n", getpid());

  init_attributes();
  init_mail();

  if ( init_game(def_db_in, def_db_out) < 0 ) {
    log_error(tprintf("Couldn't load %s!", def_db_in));
    exit_nicely(136);
  }
  
  set_signals();
  
  save_muse_pid();

  time(&muse_up_time);
  
  /* main engine call */
  shovechars(tinyport);
  
  log_important("Shutting down normally");
  close_sockets();
  do_haltall(1);
  dump_database();
  free_database();
  free_mail();
  free_hash();
  close(sock);

  if ( sig_caught > 0 )
    log_important(tprintf("Shutting down due to signal %d", sig_caught));
  exit_nicely(exit_status);
}

void save_muse_pid()
{
  FILE *fp;
  struct stat statbuf;
  
  if (stat(MUSE_PID_FILE,&statbuf)>=0) {
    /* it already exists! */
    log_error("muse pid file already exists!");
    exit_nicely(136);
  }
  unlink(MUSE_PID_FILE);
  if ( (fp = fopen(MUSE_PID_FILE, "w")) != NULL )  {
    fprintf(fp, "%d\n", getpid());
    fclose(fp);
  }
}

void remove_muse_pid()
{
  FILE *fp;
  char buf1[20], buf2[20];

  close(reserved);

  if (!(fp = fopen(MUSE_PID_FILE, "r"))) {
    perror("muse pid file");
    log_error("can't open muse pid file!");
    return;
  }

  fgets(buf1, 20, fp);
  sprintf(buf2, "%d\n", getpid());
  if (strcmp(buf1, buf2)) {
    log_error(tprintf("muse pid file doesn't contain me(%d)!",getpid()));
    fclose(fp);
    return;
  }
  
  fclose(fp);
  unlink(MUSE_PID_FILE);

  reserved = open("/dev/null", O_RDWR, 0);
}

void init_args(argc, argv)
     int argc;
     char *argv[];
{
  /* change default input database? */
  if ( argc > 1 )
    --argc, def_db_in = *++argv;
  
  /* change default dump database? */
  if ( argc > 1 )
    --argc, def_db_out = *++argv;
  
  /* change default log file? */
  if ( argc > 1 )
    --argc, def_logfile = *++argv;
  
  /* change port number? */
  if ( argc > 1 )
    --argc, tinyport = atoi(*++argv);
}

void init_io()
{
#ifndef fileno
  /* sometimes it's #defined in stdio.h */
  extern int fileno P((FILE *));
#endif
  int fd;
  
  /* close standard input */
  fclose(stdin);
  
  /* open a link to the log file */
  fd = open(def_logfile, O_WRONLY | O_CREAT | O_APPEND, DEF_MODE);
  if ( fd < 0 )  {
    perror("open()");
    log_error(tprintf("error opening %s for writing", def_logfile));
    exit_nicely(136);
  }
  
  /* attempt to convert standard output to logfile */
  close(fileno(stdout));
  if ( dup2(fd, fileno(stdout)) == -1 )  {
    perror("dup2()");
    log_error("error converting standard output to logfile");
  }
  mklinebuf(stdout);
  
  /* attempt to convert standard error to logfile */
  close(fileno(stderr));
  if ( dup2(fd, fileno(stderr)) == -1 )  {
    perror("dup2()");
    printf("error converting standard error to logfile\n");
  }
  mklinebuf(stderr);
  
  /* this logfile reference is no longer needed */
  close(fd);
  
  /* save a file descriptor */
  reserved = open("/dev/null",O_RDWR, 0);
}
int floating_x;
void handle_exception()
{
  floating_x = 1;
}

void set_signals()
{
  /* we don't care about SIGPIPE, we notice it in select() and write() */
  signal(SIGPIPE, SIG_IGN);
#ifdef SYSV
  signal (SIGCLD, SIG_IGN);
#endif
  
  /* standard termination signals */
  signal (SIGINT, bailout);

  /* catch these because we might as well */
  /*  signal (SIGQUIT, bailout);
      signal (SIGILL, bailout);
      signal (SIGTRAP, bailout);
      signal (SIGIOT, bailout);
      signal (SIGEMT, bailout);
      signal (SIGFPE, bailout);
      signal (SIGBUS, bailout);
      signal (SIGSEGV, bailout);
      signal (SIGSYS, bailout);
      signal (SIGTERM, bailout);
      signal (SIGXCPU, bailout);
      signal (SIGXFSZ, bailout);
      signal (SIGVTALRM, bailout);
      signal (SIGUSR2, bailout);
      * want a core dump for now!! */
  
  /* status dumper (predates "WHO" command) */
  signal(SIGUSR1, dump_status);
  
  signal (SIGHUP, sig_handler);
  signal (SIGTERM, sig_handler);
  signal (SIGEMT, handle_exception);
}

void sig_handler(sig)
     int sig;
{
  sig_caught = sig;
  if(sig == 15)
    exit_status = 0;
  else
    exit_status = 1;
  shutdown_flag = 1;
}

void raw_notify(player,msg)
     dbref player;
 char *msg;
{
  struct descriptor_data *d;

  if (!(db[player].flags & PLAYER_CONNECT))
    return;
  for(d = descriptor_list; d; d = d->next) {
    if (d->state==CONNECTED && d->player == player) {
      queue_string(d, msg);
      queue_write(d, "\n", 1);
    }
  }
}

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

int 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 = 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);
}

int need_more_proc;
int maxd;

void shovechars(port)
     int port;
{
  fd_set input_set, output_set;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval timeout, slice_timeout;
  int found;
  struct descriptor_data *d, *dnext;
  struct descriptor_data *newd;
  int avail_descriptors;

  log_io(tprintf("Starting up on port %d",port));
  
  sock = make_socket (port);
  maxd = sock+1;
  gettimeofday(&last_slice, (struct timezone *) 0);
  
  avail_descriptors = getdtablesize() - 5;
  
#ifdef USE_RWHO
  rwhocli_setup(RWHO_SERVER,RWHO_PASSWORD,MUSE_NAME,BASE_VERSION);
#endif

  while (shutdown_flag == 0) {
    gettimeofday(&current_time, (struct timezone *) 0);
    last_slice = update_quotas (last_slice, current_time);
    if (loading_db) {
      process_commands();
      
      if (shutdown_flag)
	break;
    
      /* test for events */
      dispatch();
    } else {
      load_more_db();
    }
    /* any queued robot commands waiting? */
    timeout.tv_sec = (need_more_proc||test_top()) ? 0 : 100;
    need_more_proc=0;
    timeout.tv_usec = 5;
    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 && sock>=0)
      FD_SET (sock, &input_set);
    
    for (d = descriptor_list; d; d=dnext) {
      dnext = d->next;
      if(d->cstatus&C_REMOTE && d->output.head) {
	if(!process_output(d))
	  shutdownsock(d);
	need_more_proc=1;
      }
    }
    
    for (d = descriptor_list; d; d=d->next)
      if(!(d->cstatus&C_REMOTE)) {
	if (d->input.head)
	  timeout = slice_timeout;
	else
	  FD_SET (d->descriptor, &input_set);
	if (d->output.head && (d->state!=CONNECTED || d->player>0))
	  FD_SET (d->descriptor, &output_set);
      }
    
    if ((found=select (maxd, &input_set, &output_set,
		       (fd_set *) 0, &timeout)) < 0) {
      if (errno != EINTR) {
	perror ("select");
	/*	return;*/ /* naw.. stay up. */
      }
    } else {
      /* if !found then time for robot commands */
      if (loading_db && !found) {
	if(do_top() && do_top())
	  do_top();
	continue;
      }
      (void) time (&now);
      if (sock>=0 && FD_ISSET (sock, &input_set)) {
	if (!(newd = new_connection (sock))) {
	  if (errno
	      && errno != EINTR
	      && errno != EMFILE
	      && errno != ENFILE) {
	    perror ("new_connection");
	    /*	    return;*/ /* naw.. stay up. */
	  }
	} else {
	  if (newd->descriptor >= maxd)
	    maxd = newd->descriptor + 1;
	}
      }
      for (d = descriptor_list; d; d=dnext) {
	dnext=d->next;
	if (FD_ISSET (d->descriptor, &input_set) && !(d->cstatus&C_REMOTE))
	  if (!process_input (d))
	    shutdownsock(d);
      }
      for (d = descriptor_list; d; d = dnext) {
	dnext = d->next;
	if(d->cstatus&C_REMOTE)
	  process_output(d);
      }
      for (d = descriptor_list; d; d = dnext) {
	dnext = d->next;
	
	if(!(d->cstatus&C_REMOTE)
	   && (FD_ISSET (d->descriptor, &output_set)))
	  if (!process_output(d))
	    shutdownsock(d);
      }
      for (d = descriptor_list; d; d = dnext) {
	dnext = d->next;
	if(d->cstatus&C_REMOTE &&
	   !d->parent)
	  shutdownsock(d);
      }
    }
  }
}

struct descriptor_data *new_connection(sock)
     int sock;
{
  int newsock;
  struct sockaddr_in addr;
  int addr_len;

  addr_len = sizeof (addr);
  newsock = accept (sock, (struct sockaddr *) & addr, &addr_len);
  if (newsock < 0) {
    if(errno==EALREADY) {	/* screwy sysv buf. */
      static int k=0;
      if(k++>50) {
	log_error("Killin' EALREADY. restartin' socket.");
	puts("Killin' EALREADY. restartin' socket.");
	close(sock);
	sock = make_socket(4201);
	k=0;
      }
    }
    return 0;
#ifdef LOCKOUT
  } else if(forbidden_site(ntohl(addr.sin_addr.s_addr))) {
    log_io(tprintf("REFUSED CONNECTION from %s(%d) on descriptor %d",
		   addrout(addr.sin_addr.s_addr),
		   ntohs(addr.sin_port), newsock));
    shutdown(newsock, 0);
    close(newsock);
    errno = 0;
    return 0;
#endif /* LOCKOUT */
  } else {
    struct hostent *hent;
    char buff[100];
    time_t tt;
    hent = gethostbyaddr (&(addr.sin_addr.s_addr),
			  sizeof (addr.sin_addr.s_addr), AF_INET);
    if (hent)
      strcpy (buff, hent->h_name);
    else {
      extern char *inet_ntoa();
      strcpy(buff, inet_ntoa(addr.sin_addr.s_addr));
    }
    tt = time(NULL);
    log_io(tprintf("USER CONNECT: des: %d host %s time: %s", newsock,buff,ctime(&tt)));
    return initializesock (newsock, &addr,buff);
  }
}

 char *addrout(a)
     long a;
{
#ifdef HOST_NAME
  extern char *inet_ntoa();
  struct hostent *he;
  
  a = htonl(a);
  he = gethostbyaddr(&a,sizeof(a),AF_INET);
  if (he)
    return he->h_name;
  else
    return inet_ntoa(a);
#else
  /* return inet_ntoa(a);*/
  return "OOPS";
#endif /* HOST_NAME */
}


void clearstrings(d)
     struct descriptor_data *d;
{
  if (d->output_prefix) {
    FREE(d->output_prefix);
    d->output_prefix = 0;
  }
  if (d->output_suffix) {
    FREE(d->output_suffix);
    d->output_suffix = 0;
  }
}

void shutdownsock(d)
     struct descriptor_data *d;
{
  int count;
  dbref guest_player;
  struct descriptor_data *sd;
  
  /* if this is a guest player, save his reference # */
  guest_player = NOTHING;
  if (d->state == CONNECTED)
    if(d->player>0)
      if(Guest(d->player))
	guest_player = d->player;
  
  if (d->state == CONNECTED) {
    if(d->player>0) {
      log_io (tprintf("DISCONNECT descriptor %d player %s(%d)",
		      d->descriptor, db[d->player].name, d->player));
      announce_disconnect(d->player);
    }
  } else {
    log_io (tprintf("DISCONNECT descriptor %d never connected",
		    d->descriptor));
  }
  
  clearstrings(d);
  if(!(d->cstatus&C_REMOTE)) {
    shutdown(d->descriptor, 0);
    close(d->descriptor);
  } else {
    register struct descriptor_data *k;
    for(k=descriptor_list; k; k=k->next)
      if(k->parent==d)
	k->parent = 0;
  }
  freeqs(d);
  *d->prev = d->next;
  if (d->next)
    d->next->prev = d->prev;
  if(!(d->cstatus&C_REMOTE))
    ndescriptors--;
  
  FREE(d);
  
  /* if this is a guest account and the
     last to disconnect from it, kill it */
  if ( guest_player != NOTHING )  {
    count = 0;
    for (sd = descriptor_list; sd; sd = sd->next) {
      if (sd->state == CONNECTED && sd->player == guest_player)
	++count;
    }
    if ( count == 0 )
      destroy_guest(guest_player);
  }
}
void rwho_setupfd(player,fd)
     dbref player;
     int fd;
{
  struct descriptor_data *d;

  ndescriptors++;
  MALLOC(d, struct descriptor_data, 1);
  d->descriptor = fd;
  d->concid = make_concid();
  d->cstatus = 0;
  d->parent = 0;
  d->state = CONNECTED;
  make_nonblocking(fd);
  d->player = -player;
  d->output_prefix=0;
  d->output_suffix=0;
  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 = 0;
  d->raw_input_at = 0;
  d->quota=COMMAND_BURST_SIZE;
  d->last_time=0;
  /*  d->address=inet_addr("127.0.0.1"); * localhost. cuz. */
  strcpy(d->addr,"RWHO");
  if (descriptor_list)
    descriptor_list->prev = &d->next;
  d->next = descriptor_list;
  d->prev = &descriptor_list;
  d->edit_buff = NULL;
  descriptor_list = d;
  if (fd >= maxd)
    maxd = fd + 1;
}

struct descriptor_data *initializesock(s,a,addr)
     int s;
     struct sockaddr_in *a;                        
     char *addr;
{
  struct descriptor_data *d;
  /*fprintf(stderr,"3\n");fflush(stderr);*/
  ndescriptors++;
  MALLOC(d, struct descriptor_data, 1);
  d->descriptor = s;
  d->concid = make_concid();
  d->cstatus = 0;
  d->parent = 0;
  d->state = WAITCONNECT;
  make_nonblocking (s);
  d->output_prefix = 0;
  d->output_suffix = 0;
  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 = 0;
  d->raw_input_at = 0;
  d->quota = COMMAND_BURST_SIZE;
  d->last_time = 0;
  strncpy(d->addr,addr,50);
  d->address = *a;			/* added 5/3/90 SCG */
  if (descriptor_list)
    descriptor_list->prev = &d->next;
  d->next = descriptor_list;
  d->prev = &descriptor_list;
  d->edit_buff = NULL;
  descriptor_list = d;
  welcome_user (d);                    
  return d;
}

int make_socket(port)
     int port;
{
  int s;
  struct sockaddr_in server;
  int opt;
  
  s = socket (AF_INET, SOCK_STREAM, 0);
  if (s < 0) {
    perror (tprintf("creating stream socket on port %d",port));
#ifndef RESOCK
    exit_status = 1;		/* try again. */
    shutdown_flag = 1;
#endif
    return -1;
  }
  opt = 1;
  if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
		  (char *) &opt, sizeof (opt)) < 0) {
    perror ("setsockopt");
#ifndef RESOCK
    shutdown_flag = 1;
    exit_status = 1;
#endif
    close(s);
    return -1;
  }
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = INADDR_ANY;
  server.sin_port = htons (port);
  if (bind (s, (struct sockaddr *) & server, sizeof (server))) {
    perror ("binding stream socket");
    close (s);
#ifndef RESOCK
    shutdown_flag = 1;
    exit_status = 1;
#endif
    return -1;
  }
  listen (s, 5);
  return s;
}

struct text_block *make_text_block(s,n)
 char *s;
     int n;
{
  struct text_block *p;
  
  MALLOC(p, struct text_block, 1);
  MALLOC(p->buf, char, n);
  bcopy (s, p->buf, n);
  p->nchars = n;
  p->start = p->buf;
  p->nxt = 0;
  return p;
}

void free_text_block (t)
     struct text_block *t;
{
  FREE (t->buf);
  FREE ((char *) t);
}

void add_to_queue(q,b,n)
     struct text_queue *q;
 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;
    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;
 char *b;
     int n;
{
  int space;
  if (d->cstatus & C_REMOTE)
    need_more_proc=1;
  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;
 char *s;
{
  return queue_write (d, s, strlen (s));
}

int process_output(d)
     struct descriptor_data *d;
{
  struct text_block **qp, *cur;
  int cnt;
  
  if(d->cstatus&C_REMOTE) {
    static char buf[10];
    static char obuf[2048];
    int buflen;
    int k,j;
    sprintf(buf,"%d ",d->concid);
    buflen=strlen(buf);
    
    bcopy(buf,obuf,buflen);
    j=buflen;
    
    for(qp= &d->output.head;(cur= *qp);) {
      need_more_proc=1;
      for(k=0;k<cur->nchars;k++) {
	obuf[j++]=cur->start[k];
	if(cur->start[k]=='\n') {
	  if(d->parent)
	    queue_write(d->parent,obuf,j);
	  bcopy(buf,obuf,buflen);
	  j=buflen;
	}
      }
      d->output_size -= cur->nchars;
      if(!cur->nxt)
	d->output.tail=qp;
      *qp = cur->nxt;
      free_text_block(cur);
    }
    if(j>buflen)
      queue_write(d,obuf+buflen,j-buflen);
    return 1;
  } else {
    for (qp = &d->output.head; (cur = *qp);) {
      cnt = write (d->descriptor, cur -> start, cur -> nchars);
      if (cnt < 0) {
	if (errno == EWOULDBLOCK)
	  return 1;
	return 0;
      }
      d->output_size -= cnt;
      if (cnt == cur -> nchars) {
	if (!cur -> nxt)
	  d->output.tail = qp;
	*qp = cur -> nxt;
	free_text_block (cur);
	continue;		/* do not adv ptr */
      }
      cur -> nchars -= cnt;
      cur -> start += cnt;
      break;
    }
  }
  return 1;
}

void make_nonblocking(s)
     int s;
{
  if (fcntl (s, F_SETFL, FNDELAY) == -1) {
    perror ("make_nonblocking: fcntl");
    panic ("FNDELAY fcntl failed");
  }
}

void freeqs(d)
     struct descriptor_data *d;
{
  struct text_block *cur, *next;
  
  cur = d->output.head;
  while (cur) {
    next = cur -> nxt;
    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;
    free_text_block (cur);
    cur = next;
  }
  d->input.head = 0;
  d->input.tail = &d->input.head;
  
  if (d->raw_input)
    FREE (d->raw_input);
  d->raw_input = 0;
  d->raw_input_at = 0;
}

void welcome_user(d)
     struct descriptor_data *d;
{
  connect_message(d, WELCOME_MSG_FILE,0);
}

void connect_message(d, filename, direct)
     struct descriptor_data *d;
     char *filename;
     int direct;
{
  int n, fd;
  char buf[512];
  close(reserved);
  
  if ( (fd = open(filename, O_RDONLY, 0)) != -1 )  {
    while ( (n = read(fd, buf, 512)) > 0 )
      queue_write(d, buf, n);
    close(fd);
    queue_write(d, "\n", 1);
  }
  reserved=open("/dev/null", O_RDWR, 0);
  if(direct) {
    process_output(d);
  }
}

char *strsave (s)
 char *s;
{
  char *p;
  
  MALLOC (p, char, strlen(s) + 1);
  
  if (p)
    strcpy (p, s);
  return p;
}

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

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

  got = read (d->descriptor, buf, sizeof buf);
  if (got <= 0)
    return 0;
  if (!d->raw_input) {
    MALLOC(d->raw_input,char,MAX_COMMAND_LEN);
    d->raw_input_at = d->raw_input;
  }
  p = d->raw_input_at;
  pend = d->raw_input + MAX_COMMAND_LEN - 1;
  for (q=buf, qend = buf + 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 (p < pend && isascii (*q) && isprint (*q)) {
      *p++ = *q;
    }
  }
  if(p > d->raw_input) {
    d->raw_input_at = p;
  } else {
    FREE(d->raw_input);
    d->raw_input = 0;
    d->raw_input_at = 0;
  }
  return 1;
}

void set_userstring (userstring,command)
     char **userstring;
 char *command;
{
  if (*userstring) {
    FREE(*userstring);
    *userstring = 0;
  }
  while (*command && isascii (*command) && isspace (*command))
    command++;
  if (*command)
    *userstring = strsave (command);
}

void process_commands()
{
  int nprocessed;
  struct descriptor_data *d, *dnext;
  struct text_block *t;
  do {
    nprocessed = 0;
    for (d = descriptor_list; d; d = dnext) {
      dnext = d->next;
      if (d -> quota > 0 && (t = d -> input.head)) {
	nprocessed++;
	if (!do_command(d, t -> start)) {
	  connect_message(d,LEAVE_MSG_FILE,1);
	  shutdownsock(d);
	} else {
	  d -> input.head = t -> nxt;
	  if (!d -> input.head)
	    d -> input.tail = &d -> input.head;
	  free_text_block (t);
	}
      }
    }
  } while (nprocessed > 0);
}

int do_command (d,command)
     struct descriptor_data *d;
     char *command;
{
  d->last_time = now;
  d->quota--;
  depth=0;
  if (!*command && !(d->player<0 && d->state==CONNECTED))
    return 1;
  if (d->state == CONNECTED && d->player > 0) {
    static char buf[2048];
    sprintf(buf,"%05d: %s",d->player,command);
    log_typed(buf);
  }
  if (!strcmp (command, QUIT_COMMAND)) {
    return 0;
  } else if(!strncmp (command, "I wanna be a concentrator... my password is ",
		      sizeof ("I wanna be a concentrator... my password is ")
		      -1)) {
    do_becomeconc(d,command+sizeof("I wanna be a concentrator... my password is ")-1);
  } else if (! strncmp(command, WHO_COMMAND, sizeof(WHO_COMMAND))
	     && d->state != CONNECTED) {
    if (d->output_prefix) {
      queue_string (d, d->output_prefix);
      queue_write (d, "\n", 1);
    }
    dump_users (NOTHING,NULL,NULL,d);
    if (d->output_suffix) {
      queue_string (d, d->output_suffix);
      queue_write (d, "\n", 1);
    }
  } 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 (d->cstatus & C_CCONTROL) {
      if(!strcmp(command, "Gimmie a new concid"))
	do_makeid(d);
      else if(!strncmp(command, "I wanna connect concid ",
		       sizeof  ("I wanna connect concid ")-1)) {
	char *m,*n;
	m=command+sizeof("I wanna connect concid ")-1;
	n=strchr(m,' ');
	if(!n)
	  queue_string(d,"Usage: I wanna connect concid <id> <hostname>\n");
	else
	  do_connectid(d,atoi
		       (command+
			sizeof   ("I wanna connect concid ")-1),n);
      } else if(!strncmp(command, "I wanna kill concid ",
			 sizeof  ("I wanna kill concid ")-1))
	do_killid(d,
		  atoi(command+
		       sizeof  ("I wanna kill concid ")-1));
      else {
	char *k;
	k=strchr(command,' ');
	if(!k)
	  queue_string(d,"Huh???\r\n");
	else {
	  struct descriptor_data *l;
	  int j;
	  
	  *k = '\0';
	  j=atoi(command);
	  for(l=descriptor_list;l;l=l->next) {
	    if(l->concid==j)
	      break;
	  }
	  if(!l)
	    queue_string(d,"I don't know that concid.\r\n");
	  else {
	    k++;
	    if(!do_command(l,k)) {
	      connect_message(l,LEAVE_MSG_FILE,1);
	      shutdownsock(l);
	    }
	  }
	}
      }
    } else {
      if (d->state == CONNECTED) {
	if (d->output_prefix) {
	  queue_string (d, d->output_prefix);
	  queue_write (d, "\n", 1);
	}           
	cplr=d->player;
	if(d->player>0) {
	  strcpy(ccom,command);
	  if (d->edit_buff == NULL) 
	    process_command (d->player, command,NOTHING);
	  else
	    edit_command(d, d->player, command);
	} else
	  notify(-d->player,command);
	if (d->output_suffix) {
	  queue_string (d, d->output_suffix);
	  queue_write (d, "\n", 1);
	}
      } else {
	check_connect (d, command);
      }
    }
  return 1;
}

void check_connect (d,msg)
     struct descriptor_data *d;
 char *msg;
{
  extern NALLOC *glurp;
  char *p;
  char command[MAX_COMMAND_LEN];
  char user[MAX_COMMAND_LEN];
  char password[MAX_COMMAND_LEN];
#ifdef EMAIL_CREATE
  char email[MAX_COMMAND_LEN];
#endif
  dbref player;
  
  if (d->state == WAITPASS) {
    /* msg contains the password. */
    char *foobuf = na_alloc(glurp, MAX_COMMAND_LEN);
    sprintf(foobuf,"connect %s %s",d->charname, msg);
    free(d->charname);
    queue_string(d,got_password);
    d->state = WAITCONNECT;
    msg = foobuf;
  }

  parse_connect (msg, command, user, password
#ifdef EMAIL_CREATE
		 ,email
#endif
		 );
  
  if (!strncmp (command, "co", 2)) {
    if ( string_prefix(user, GUEST_PREFIX) )  {
      if ( (p = make_guest(d)) == NULL )
	return;
      strcpy(user, p);
      strcpy(password, GUEST_PREFIX);
    }
    player = connect_player (user, password);
    if (player == NOTHING && !*password) {
      /* hmm. bet they want to type it in seperately. */
      queue_string(d, get_password);
      d->state = WAITPASS;
      d->charname = malloc(strlen(user)+1);
      strcpy(d->charname, user);
      return;
    } else if (player == NOTHING) {
      queue_string (d, connect_fail);
      log_io (tprintf("FAILED CONNECT %s on descriptor %d",
		      user, d->descriptor));
    } else {
      log_io (tprintf("CONNECTED %s(%d) on descriptor %d",
		      db[player].name, player, d->descriptor));
      if (d->state == WAITPASS)
	queue_string(d, got_password);
      d->state = CONNECTED;
      d->connected_at = time(0);
      d->player = player;
      /* give players a message on connection */
      connect_message(d, CONNECT_MSG_FILE,0);
      announce_connect(player);
      do_look_around(player);
      /* XXX might want to add check for haven here. */

      if(Guest(player))
	notify(player, tprintf(
			       "Welcome to %s; your name is %s",
			       MUSE_NAME, db[player].name));
    }
  }
  else if (!strncmp (command, "cr", 2)) {
#ifdef WCREAT
    connect_message(d, REGISTER_MSG_FILE, 0);
#else /* WCREAT */
#ifdef EMAIL_CREATE
    if(!email || !strcmp(email,"")) {
      queue_string(d,"You need to specify an email address.\n");
      queue_string(d,"Try: create <name> <password> <email address>.\n");
    } else
#endif
      {
	player = create_player (user, password,CLASS_VISITOR);
	if (player == NOTHING) {
	  queue_string (d, create_fail);
	  log_io (tprintf("FAILED CREATE %s on descriptor %d",
			  user, d->descriptor));
	} else {
	  log_io (tprintf("CREATED %s(%d) on descriptor %d",
			  db[player].name, player, d->descriptor));
#ifdef EMAIL_CREATE
	  atr_add(player,A_EMAIL,email);
#endif
	  d->connected = 1;
	  d->connected_at = time(0);
	  d->player = player;
	  /* give new players a special message */
	  connect_message(d, NEW_CONNECT_MSG_FILE, 0);
	  /* connect_message(d, CONNECT_MSG_FILE); */
	  announce_connect(player);
	  do_look_around(player);
	}
      }
#endif /* WCREAT */
  } else
    welcome_user (d);
  if (d->state == WAITPASS) {
    d->state = WAITCONNECT;
    queue_string(d, got_password);
  }
}

void parse_connect (msg,command,user,pass
#ifdef EMAIL_CREATE
		    ,email
#endif
		    )
 char *msg;
     char *command;
     char *user;
     char *pass;
#ifdef EMAIL_CREATE
     char *email;
#endif
{
  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;
  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';
#ifdef EMAIL_CREATE
  while (*msg && isascii(*msg) && isspace(*msg))
    msg++;
  p=email;
  while (*msg && isascii(*msg) && !isspace(*msg))
    *p++ = *msg++;
  *p = '\0';
#endif    
}

void mark_free(n)
     int n;
{
  guest_free[n-1] = 1;
}

/* this algorithm can be changed later to accomodate an unlimited
   number of guests.  currently, it supports a limited number.    */
char *make_guest(d)
     struct descriptor_data *d;
{
  int i;
  dbref player;
  char name[50];
  static int need_init = 1;
  
  /* make sure flags are initialized */
  if ( need_init )  {
    need_init = 0;
    for ( i = 0; i < NUMBER_GUESTS; i++ )
      guest_free[i] = 1;
  }
  
  /* locate a free guest id - mark it non free */
  for ( i = 0; i < NUMBER_GUESTS; i++ )
    if ( guest_free[i] )
      break;
  if ( i == NUMBER_GUESTS )  {
    queue_string(d, "All guest ID's are busy; please try again later.\n");
    return NULL;
  }
  
  sprintf(name, "%s%d", GUEST_PREFIX, i+1);
  player = create_guest(name, GUEST_PREFIX);
  guest_free[i] = 0;
  
  if ( player == NOTHING )  {
    queue_string(d, "Error creating guest ID, please try again later.\n");
    log_error(tprintf("Error creating guest ID.  '%s' already exists.",
		      name));
    return NULL;
  }
  
  return db[player].name;
}

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

/*  shutdown_flag = 1;		* just in case. so announce_disconnect
				 * does'nt reset the PLAYER_CONNECT
				 * flags. */
  
  for (d = descriptor_list; d; d = dnext) {
    dnext = d->next;
    if(!(d->cstatus&C_REMOTE)) {
      write (d->descriptor, shutdown_message, strlen (shutdown_message));
      process_output(d);
      shutdownsock(d);
    }
  }
}

void emergency_shutdown()
{
  log_error("Emergency shutdown.");
  shutdown_flag = 1;
  exit_status = 136;
  close_sockets();
}

int boot_off(player)
     dbref player;
{
  struct descriptor_data *d;
  
  for (d = descriptor_list; d; d = d->next) {
    if (d->state == CONNECTED && d->player == player) {
      process_output (d);
      shutdownsock(d);
      return 1;
    }
  }
  return 0;
}

signal_type bailout(sig)
     int sig;
{
  char message[1024];
  
  sprintf (message, "BAILOUT: caught signal %d", sig);
  panic(message);
  exit_nicely (136);
#ifdef void_signal_type
  return;
#else
  return 0;
#endif
}

signal_type dump_status(int i)
{
  struct descriptor_data *d;
  long now;
  
  now = time (NULL);
  fprintf (stderr, "STATUS REPORT:\n");
  for (d = descriptor_list; d; d = d->next) {
    if (d->state == CONNECTED) {
      fprintf (stderr, "PLAYING descriptor %d player %s(%d)",
	       d->descriptor, db[d->player].name, d->player);
      
      if (d->last_time)
	fprintf (stderr, " idle %d seconds\n",
		 now - d->last_time);
      else
	fprintf (stderr, " never used\n");
    } else {
      fprintf (stderr, "CONNECTING descriptor %d", d->descriptor);
      if (d->last_time)
	fprintf (stderr, " idle %d seconds\n",
		 now - d->last_time);
      else
	fprintf (stderr, " never used\n");
    }
  }
#ifdef void_signal_type
  return;
#else
  return 0;
#endif
}
extern char *wholev();

#define  WHO_BUF_SIZ	300
#define  DEF_SCR_COLS	78	  /* must be less than WHO_BUF_SIZ */
#define  MIN_SEC_SPC	2
#define  MIN_GRP_SPC	4

#define  DEF_WHO_FLAGS	"Nfoitd"  /* close to the old WHO output format */
#define  DEF_WHO_ALIAS	""	  /* what people see if no alias is set */

#define  W_NAME		0x001
#define  W_ALIAS	0x002
#define  W_FLAGS	0x004
#define  W_ONFOR	0x008
#define  W_IDLE		0x010
#define  W_CLASS	0x020
#define  W_HOST		0x040
#define  W_TALENT	0x080
#define  W_DOING        0x100

static char who_flags[] = "nafoichtd";

static char *who_fmt_small[] = {
  "%-10.10s  ", "%-10.10s  ", "%-4.4s ",
  "%9.9s  ", "%4.4s  ", "%-5.5s  ", "%-20.20s  ", "%-15.15s  ",
  "%-15.15s  "
};
static int  who_sizes_small[] = { 10, 10, 3, 9, 4, 5, 20, 15, 15 };

static char *who_fmt_large[] = {
  "%-16.16s  ", "%-16.16s  ", "%-4.4s ", "%9.9s  ", "%4.4s  ", "%-13.13s  ",
#ifdef BRACKET_HOST
  "%-32.32s  ", "%-15.15s  ", "%-15.15s  " };
  static int  who_sizes_large[] = { 16, 16, 3, 9, 4, 13, 32, 15, 15 };
  char who_bh_buf[33];
#else /* BRACKET_HOST */
  "%-30.30s  ", "%-15.15s  ", "%-15.15s  " };
  static int  who_sizes_large[] = { 16, 16, 3, 9, 4, 13, 30, 15, 15 };
#endif /* BRACKET_HOST */

#define  WHO_SIZE	9
static int who_sizes[WHO_SIZE];
static char *who_fmt[WHO_SIZE];

void dump_users(w,arg1,arg2,k)
     dbref w;
     char *arg1, *arg2;
     struct descriptor_data *k;  /* if non-zero, use k instead of w */
{
  char *p, *b, *names;
  char buf[WHO_BUF_SIZ+1];
  struct descriptor_data *d;
  char fbuf[5], flags[WHO_SIZE+1];
  int done, pow_who, hidden = 0, see_player;
  int i, j, ni, bit, bits, num_secs, scr_cols, hc, header;
  int grp, grp_len, num_grps, total_count, hidden_count;
  dbref who, nl_size, *name_list;

  strcpy(flags,DEF_WHO_FLAGS);

  /* setup special data */
  names = "";
  scr_cols = DEF_SCR_COLS;
  if( ! k )  {
    if( Typeof(w) != TYPE_PLAYER && ! payfor(w, 50) ) {
      notify(w, "You don't have enough pennies.");
      return;
    }

    if ( *(p = atr_get(w, A_COLUMNS)) != '\0' )  {
      scr_cols = atoi(p);
      if ( scr_cols > WHO_BUF_SIZ+1 )
	scr_cols = WHO_BUF_SIZ;
    }
    if ( *(p = atr_get(w, A_WHOFLAGS)) != '\0' )  {
      *flags = '\0';
      strncat(flags, p, WHO_SIZE);
    }
    /* in case this value used, we must process
       names before atr_get is called again */
    if ( *(p = atr_get(w, A_WHONAMES)) != '\0' )
      names = p;
  }

  /* override data */
  if ( arg1 != NULL )
    if ( *arg1 != '\0' )  {
      *flags = '\0';
      strncat(flags, arg1, WHO_SIZE);
    }
  if ( arg2 != NULL )
    if ( *arg2 != '\0' )
      names = arg2;

  /* Check for power to distinguish class names */
  pow_who = ( k ) ? 0 : has_pow(w, NOTHING, POW_WHO);

  /* process flags */
  bits = 0;
  grp_len = 0;
  num_secs = 0;
  for ( p = flags; *p != '\0'; p++ )  {
    for ( i = 0, bit = 1; i < WHO_SIZE; i++, bit <<= 1 )
      if ( to_lower(*p) == who_flags[i] )  {
	++num_secs;
	bits |= bit;
	if ( islower(*p) )  {
	  who_sizes[i] = who_sizes_small[i];
	  who_fmt[i]   = who_fmt_small[i];
	}
	else  {
	  who_sizes[i] = who_sizes_large[i];
	  who_fmt[i]   = who_fmt_large[i];
	}
	grp_len += who_sizes[i];
	break;
      }
    if ( i == WHO_SIZE )  {
      sprintf(buf, "%c: bad who flag.%s", (int) *p, (k) ? "\n" : "");
      if ( k )
	queue_string(k, buf);
      else
	notify(w, buf);
      return;
    }
  }

  /* process names (before atr_get called that overwrites static buffer) */
  if ( *names == '\0' )  {
    /* avoid error msg in lookup_players() for missing name list */
    nl_size = 0;
    name_list = &nl_size;
  }
  else {
    name_list = lookup_players((k) ? NOTHING : w, names);
    if (!name_list[0])
      return;			/* we tried, but no users matched. */
  }

  /* add in min section spacing */
  grp_len += (num_secs - 1) * MIN_SEC_SPC;

  /* calc # of groups */
  num_grps = ((scr_cols - grp_len) / (grp_len + MIN_GRP_SPC)) + 1;
  if ( num_grps < 1 )
    num_grps = 1;

  /* calc header size based on see-able users */
  for ( hc = 0, d = descriptor_list; d; d = d->next )  {
    if ( d->state != CONNECTED || d->player <= 0 )
      continue;
    if (*atr_get(d->player, A_LHIDE)) {
      if (k)
	continue;
      if (!controls(w,d->player,POW_WHO) && !could_doit(w,d->player,A_LHIDE))
	continue;
    }
    if ( name_list[0] > 0 )  {
      for ( i = 1; i <= name_list[0]; i++ )
	if ( d->player == name_list[i] )
	  break;
      if ( i > name_list[0] )
	continue;
    }
    if ( ++hc >= num_grps )
      break;
  }

  /* process names or, (if no names requested) loop once for all names */
  b = buf;
  grp = 1;
  header = 1;
  ni = 1;
  who = NOTHING;
  done = 0;
  total_count = hidden_count = 0;
  while ( !done )  {
    /* if a player is on the names list, process them */
    if ( ni <= name_list[0] )
      who = name_list[ni++];
    /* if no names left on list, this is final run through loop */
    if ( ni > name_list[0] )
      done = 1;

    /* Build output lines and print them */
    d = descriptor_list;
    while ( d )  {
      if ( !header )  {
	/* skip records that aren't in use */
	/* if name list, skip those not on list */
	if ( d->state!=CONNECTED || d->player <= 0 ||
	     ( who != NOTHING && who != d->player) )  {
	  d = d->next;
	  continue;
	}

	/* handle hidden players, keep track of player counts */
	hidden = 0;
        see_player = 1;
	
	if (k?*atr_get(d->player,A_LHIDE):!could_doit(w,d->player,A_LHIDE)) {
	  hidden = 1;
	  see_player = !k && controls(w, d->player, POW_WHO);
	}

	/* do counts */
	if ( see_player || name_list[0] == 0 )  {
	  ++total_count;
	  if ( hidden )
	    ++hidden_count;
	}

	if ( !see_player )  {
	  d = d->next;
	  continue;
	}
      }

      /* build output line */
      for ( i = 0, bit = 1; i < WHO_SIZE; i++, bit <<= 1 )  {
	switch ( bits & bit )  {
	  case W_NAME:
	    p = header ? "Name" : db[d->player].name;
	    break;
	  case W_ALIAS:
	    if ( header )
	      p = "Alias";
	    else if ( *(p = atr_get(d->player, A_ALIAS)) == '\0' )
	      p = DEF_WHO_ALIAS;
	    break;
	  case W_FLAGS:
	    if ( header )
	      p = "Flg";
	    else  {
	      fbuf[0] = (hidden)				? 'h' : ' ';
	      fbuf[1] = (k?*atr_get(d->player,A_LPAGE):
			 !could_doit(w,d->player,A_LPAGE))	? 'H' : ' ';
	      fbuf[2] = (db[d->player].flags & PLAYER_NO_WALLS) ? 'N' : ' ';
	      fbuf[3] = (db[d->player].flags & PLAYER_NEWBIE)   ? 'n' : ' ';
	      fbuf[4] = '\0';
	      p = fbuf;
	    }
	    break;
          case W_TALENT:
            if ( header )
              p = "Talent";
            else
	      p=atr_get(d->player,A_TALENT);
            break;
	  case W_DOING:
	    if (header)
	      p = "Doing";
	    else
	      p=atr_get(d->player,A_DOING);
	    break;
	  case W_ONFOR:
	    p = header ? "On For" : time_format_1(now - d->connected_at);
	    break;
	  case W_IDLE:
	    p = header ?   "Idle" : time_format_2(now - d->last_time);
	    break;
	  case W_CLASS:
	    if ( header )
	      p = "Class";
	    else
	      if(pow_who)
		p = wholev(d->player,(who_sizes[i]>who_sizes_small[i]),pow_who);
	      else
		p = "?";
	    break;
	  case W_HOST:
	    p = "?";
	    if ( header )
	      p = "Hostname";
	    else if ( !k )
	      if ( has_pow(w, d->player, POW_HOST) )  {
	        p = d->addr;
#ifdef BRACKET_HOST
		if ( who_sizes[i] > who_sizes_small[i] )  {
		  who_bh_buf[0] = '[';
		  for ( j = 1, p = d->addr; j <= 30 && *p != '\0'; j++, p++ )
		    who_bh_buf[j] = *p;
		  who_bh_buf[j++] = ']';
		  who_bh_buf[j] = '\0';
		  p = who_bh_buf;
		}
#endif /* BRACKET_HOST */
	      }
	    break;
	  default:
	    continue;
	}
	sprintf(b, who_fmt[i], p);
	b += who_sizes[i] + 2;
      }
      b -= 2;

      if ( header )
	--hc;

      if ( (!header && ++grp <= num_grps) || (header && hc) )  {
	/* make space between groups */
	strcpy(b, "    ");
	b += 4;
      }
      else  {
	/* trim excess spaces */
	while ( *--b == ' ' )
	  ;

	/* print output line */
	if ( k )  {
	  *++b = '\n';
	  *++b = '\0';
	  queue_string(k, buf);
	}
	else  {
	  *++b = '\0';
	  notify(w, buf);
	}

	/* reset for new line */
	grp = 1;
	b = buf;
      }

      if ( header )  {
	if ( grp == 1 && hc <= 0 )
	  hc = header = 0;
      }
      else
	d = d->next;
    }
  }

  /* print last line (if incomplete) */
  if ( grp > 1 )  {
    /* trim extra spaces */
    while ( *--b == ' ' )
      ;

    if ( k )  {
      *++b = '\n';
      *++b = '\0';
      queue_string(k, buf);
    }
    else  {
      *++b = '\0';
      notify(w, buf);
    }
  }

  b = buf;
  if ( hidden_count > 0 )  {
    sprintf(b, "Hidden Users%s: %d   ",
      ( name_list[0] > 0 ) ? " Found" : "", hidden_count);
    b = b + strlen(b);
  }
  sprintf(b, "Total Users%s: %d.",
    ( name_list[0] > 0 ) ? " Found" : "", total_count);
  if ( k )  {
    queue_string(k, "---\n");
    queue_string(k, buf);
    queue_string(k, "\n");
  }
  else  {
    notify(w, "---");
    notify(w, buf);
  }
}

char *time_format_1(dt)
     time_t dt;
{
  struct tm *delta;
  static char buf[64];
  
  if(dt < 0) dt = 0;
  
  delta = gmtime(&dt);
  if (delta->tm_yday > 0) {
    sprintf(buf, "%dd %02d:%02d",
	    delta->tm_yday, delta->tm_hour, delta->tm_min);
  } else {
    sprintf(buf, "%02d:%02d",
	    delta->tm_hour, delta->tm_min);
  }
  return buf;
}

 char *time_format_2(dt)
time_t dt;
{
  register struct tm *delta;
  static char buf[64];
  
  if(dt < 0) dt = 0;
  
  delta = gmtime(&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;
  char buf[BUFFER_LEN];
  extern dbref speaker;
  char *s, t[30];		/* we put this here instead of in connect_player cuz we gotta */
  time_t tt;			/* notify the player and the descriptor isn't set up yet then */
  int connect_again = db[player].flags&PLAYER_CONNECT;
#ifdef USE_RWHO
  time_t tv;
  rwhocli_userlogin(tprintf("%d@%s",player,RWHO_MUSE_NAME),db[player].name,tv);
#endif
  
  if ((loc = getloc(player)) == NOTHING) return;
  
  if (connect_again)
    sprintf(buf, "%s has reconnected.", db[player].name);
  else
    sprintf(buf, "%s has connected.", db[player].name);
  
  /* added to allow player's inventory to hear a player connect */
  speaker=player;
  notify_in(player, player, buf);
  if (!IS(loc,TYPE_ROOM,ROOM_AUDITORIUM))
    notify_in(loc, player, buf);
  db[player].flags|=PLAYER_CONNECT;
  db[player].flags&=~HAVEN;
  if (!Guest(player)) {
    time(&tt);
    strcpy(t,ctime(&tt));
    t[strlen(t)-1]=0; 
    tt=atol(atr_get(player,A_LAST));
    if ( tt == 0L )
      s = "no previous login";
    else  {
      s = ctime(&tt);
      s[strlen(s)-1]=0;
      if ((strncmp(t, s, 10) != 0) &&
	  power(player, POW_MEMBER) && db[player].owner == player) {
        giveto(player, PAY_CHECK);
        notify(player, tprintf("You collect %d credits.", PAY_CHECK));
      }
    }
    notify(player,tprintf("Last login: %s", s));
    time(&tt);
    sprintf(buf, "%ld", tt);
    atr_add(player,A_LAST,buf);
    check_mail(player);
    
  }
  if (!connect_again) {
    dbref thing;
    dbref zone;
    dbref who=player;
    int depth;
    int find=0;

    did_it(who, who, NULL, NULL, A_OCONN, NULL, A_ACONN);
    did_it(who, db[who].location, NULL, NULL, NULL, NULL, A_ACONN);
    
    zone=db[0].zone;
    
    if (Typeof(db[who].location) == TYPE_ROOM) {
      zone=db[db[who].location].zone;
    } else {
      thing = db[who].location;
      for (depth = 10; depth && (find != 1); depth--, thing = db[thing].location)
	if (Typeof(thing) == TYPE_ROOM) {
	  zone=db[thing].zone;
	  find=1;
	}
    }
    
    if ((db[0].zone != zone) && (Typeof(db[0].zone) != TYPE_PLAYER))
      did_it(who, db[0].zone, NULL, NULL, NULL, NULL, A_ACONN);
      
    if (Typeof(zone) != TYPE_PLAYER)
      did_it(who, zone, NULL, NULL, NULL, NULL, A_ACONN);
      
    if ((thing = db[who].contents) != NOTHING) {
      DOLIST(thing,thing) {
	if(Typeof(thing) != TYPE_PLAYER) {
	  did_it(who, thing, NULL, NULL, NULL, NULL, A_ACONN);
	}
      }
    }
    if ((thing = db[db[who].location].contents) != NOTHING) {
      DOLIST(thing,thing) {
	if(Typeof(thing) != TYPE_PLAYER) {
	  did_it(who, thing, NULL, NULL, NULL, NULL, A_ACONN);
	}
      }
    }
  }
}

void announce_disconnect(player)
     dbref player;
{
  dbref loc;
  int num;
  char buf[BUFFER_LEN];      
  struct descriptor_data *d;
  extern dbref speaker;
  int partial_disconnect;

  if(player<0) return;

  for(num=0,d=descriptor_list;d;d=d->next)
    if (d->state==CONNECTED && (d->player>0) && (d->player==player))
      num++;
  if (num<2 && !shutdown_flag) {
    db[player].flags&=~PLAYER_CONNECT;
    atr_add(player,A_IT,"");
    partial_disconnect = 0;
  } else
    partial_disconnect = 1;

#ifdef USE_RWHO
  rwhocli_userlogout(tprintf("%d@%s",player,RWHO_MUSE_NAME));
#endif

  if ((loc = getloc(player)) != NOTHING) {
    if (partial_disconnect)
      sprintf(buf, "%s has partially disconnected.", db[player].name);
    else
      sprintf(buf, "%s has disconnected.", db[player].name);
    speaker=player;
    /* added to allow player's inventory to hear a player connect */
    notify_in(player, player, buf);
    if(!IS(loc,TYPE_ROOM,ROOM_AUDITORIUM))
      notify_in(loc, player, buf);
    if (!partial_disconnect) {
      dbref zone;
      dbref thing;
      dbref who=player;
      int depth;
      int find=0;
      
      did_it(who, who, NULL, NULL, A_ODISC, NULL, A_ADISC);
      did_it(who, db[who].location, NULL, NULL, NULL, NULL, A_ADISC);
      
      zone=db[0].zone;
      
      if (Typeof(db[who].location) == TYPE_ROOM) {
	zone=db[db[who].location].zone;
      } else {
	thing = db[who].location;
	for (depth = 10; depth && (find != 1); depth--, thing = db[thing].location)
	  if (Typeof(thing) == TYPE_ROOM) {
	    zone=db[thing].zone;
	    find=1;
	  }
      }
      
      if ((db[0].zone != zone) && (Typeof(db[0].zone) != TYPE_PLAYER))
	did_it(who, db[0].zone, NULL, NULL, NULL, NULL, A_ADISC);
      
      if (Typeof(zone) != TYPE_PLAYER)
	did_it(who, zone, NULL, NULL, NULL, NULL, A_ADISC);
      
      if ((thing = db[who].contents) != NOTHING) {
	DOLIST(thing,thing) {
	  if(Typeof(thing) != TYPE_PLAYER) {
	    did_it(who, thing, NULL, NULL, NULL, NULL, A_ADISC);
	  }
	}
      }
      if ((thing = db[db[who].location].contents) != NOTHING) {
	DOLIST(thing,thing) {
	  if(Typeof(thing) != TYPE_PLAYER) {
	    did_it(who, thing, NULL, NULL, NULL, NULL, A_ADISC);
	  }
	}
      }
    }
  }
}

#if defined(HPUX) || defined(SYSV)
#include <unistd.h>
int getdtablesize()
{
  return (int) sysconf(_SC_OPEN_MAX);
}
#endif

#ifdef SYSV
void setlinebuf(x)
     FILE *x;
{
  setbuf(x,NULL);
}
int vfork()
{
  return fork();
}
#endif
struct ctrace_int {
  struct descriptor_data *des;
  struct ctrace_int **children;
};

static struct ctrace_int *internal_ctrace(parent)
     struct descriptor_data *parent;
{
  struct descriptor_data *k;
  struct ctrace_int *op;
  extern NALLOC *glurp;
  int nchild;
  
  op=na_alloc(glurp,sizeof(struct ctrace_int));
  op->des=parent;
  if(parent && !(parent->cstatus&C_CCONTROL)) {
    op->children=na_alloc(glurp,sizeof(struct ctrace_int *));
    op->children[0]=0;
  } else {
    for(nchild=0,k=descriptor_list;k;k=k->next)
      if(k->parent == parent)
	nchild++;
    op->children=na_alloc(glurp,sizeof(struct ctrace_int *)*(nchild+1));
    for(nchild=0,k=descriptor_list;k;k=k->next)
      if(k->parent==parent) {
	op->children[nchild]=internal_ctrace(k);
	nchild++; }
    op->children[nchild]=0; }
  return op;
}

static void ctrace_notify_internal(player,d,dep)
     dbref player;
     struct ctrace_int *d;
     int dep;
{
  char buf[2000];
  int j,k;
  
  for(j=0;j<dep;j++)
    buf[j]='.';
  if(d->des && dep) {
    sprintf(buf+j,"%s descriptor: %d, concid: %d, host: %s",
	    (d->des->state==CONNECTED)
	    ?(tprintf("\"%s\"",unparse_object(player,d->des->player)))
	    :((d->des->cstatus&C_CCONTROL)
	      ?"<Concentrator Control>"
	      :"<Unconnected>"),
	    d->des->descriptor, d->des->concid,
	    d->des->addr);
    notify(player,buf);
  }
  for(k=0;d->children[k];k++)
    ctrace_notify_internal(player,d->children[k],dep+1);
}

void do_ctrace(player)
     dbref player;
{
  struct ctrace_int *dscs;
  if(!power(player,POW_HOST)) {
    notify(player,"Sorry.");
    return;
  }
  dscs=internal_ctrace(0);
  ctrace_notify_internal(player,dscs,0);
}

#ifdef RESOCK
void resock()
{
  log_io("resocking...");
  close(sock);
  sock = make_socket(TINYPORT);
  log_io("resocking done");
}
#endif