/* xsocket.c */


#include "copyright.h"

/* Patched 5/3/90 by Scott C. Goehring (gt4@mentor.cc.purdue.edu) to */
/* display hostnames instead of octets in the log and to display the */
/* hostname to wizards on a WHO command. */

/* Repatched 5/7/90 by Russ Smith (russ@uokmax.ecn.uoknor.edu) to    */
/* make the hostnames show only to ID #1 and to reverse the WHO list */
/* (that is, newest login last) as it should be.  ;-)                */

/* Patched again 5/10/90 by Scott Goehring to not reverse the WHO    */
/* list (which is REALLY how it should be :-)), to use a tabular     */
/* format for WHO, and to regularize Russ' GOD mode patches          */

/* Patches cleaned up 5/20/90 by Jim Aspnes */

#include <stdio.h>
#include <sys/types.tcp.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/errno.h>
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "db.h"
#include "interface.h"
#include "config.h"

extern int errno;
extern int reserved;
int shutdown_flag = 0;

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

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[51];
  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;
  struct sockaddr_in address;	/* added 3/6/90 SCG */
  struct descriptor_data *next;
  struct descriptor_data **prev;
} *descriptor_list = 0;

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


void process_commands();
void shovechars();
void shutdownsock();
struct descriptor_data *initializesock();
void make_nonblocking();
void freeqs();
void welcome_user();
void check_connect();
void close_sockets();
const char *addrout();
void dump_users();
void set_signals();
struct descriptor_data *new_connection();
void parse_connect();
void set_userstring();
int do_command();
char *strsave();
int make_socket();
int queue_string();
int queue_write();
int process_output();
int process_input();
int bailout();
void announce_connect();
void announce_disconnect();
const char *time_format_1();
const char *time_format_2();
#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))

#ifndef BOOLEXP_DEBUGGING
void main(argc, argv)
    int argc;
    char **argv;
{
  /* save a file descriptor */
  reserved = open("/dev/null", O_RDWR);
  if (argc < 3) {
    fprintf(stderr, "Usage: %s infile dumpfile [port]\n", *argv);
    exit(1);
  }
  if (init_game(argv[1], argv[2]) < 0) {
    fprintf(stderr, "Couldn't load %s!\n", argv[1]);
    exit(2);
  }
  setpgrp();
  set_signals();
  /* go do it */
  shovechars(argc >= 4 ? atoi(argv[3]) : TINYPORT);
  close_sockets();
  dump_database();
  exit(0);
}
#endif				/* BOOLEXP_DEBUGGING */

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

  /* standard termination signals */
  signal(SIGINT, bailout);
  signal(SIGTERM, 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);
}

void raw_notify(player, msg)
    dbref player;
    const char *msg;
{
  struct descriptor_data *d;
  if (!(db[player].flags & PLAYER_CONNECT))
    return;
  for (d = descriptor_list; d; d = d->next) {
    if (d->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;
  if (now.tv_usec < 0) {
    now.tv_usec += 1000000;
    now.tv_sec--;
  }
  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);
}

void shovechars(port)
    int port;
{
  fd_set input_set, output_set;
  long now;
  struct timeval last_slice, current_time;
  struct timeval next_slice;
  struct timeval timeout, slice_timeout;
  int maxd, found;
  struct descriptor_data *d, *dnext;
  struct descriptor_data *newd;
  int avail_descriptors;
  sock = make_socket(port);
  maxd = sock + 1;
  gettimeofday(&last_slice, (struct timezone *) 0);

  avail_descriptors = getdtablesize() - 4;

  while (shutdown_flag == 0) {
    gettimeofday(&current_time, (struct timezone *) 0);
    last_slice = update_quotas(last_slice, current_time);

    process_commands();

    if (shutdown_flag)
      break;
    /* test for events */
    dispatch();
    /* any queued robot commands waiting? */
    timeout.tv_sec = test_top() ? 0 : 1000;
    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);
    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) {
      if (errno != EINTR) {
	perror("select");
	return;
      }
    } else {
      /* if !found then time for robot commands */
      if (!found) {
	do_top() && do_top() && do_top();
	continue;
      }
      (void) time(&now);
      if (FD_ISSET(sock, &input_set)) {
	if (!(newd = new_connection(sock))) {
	  if (errno
	      && errno != EINTR
	      && errno != EMFILE
	      && errno != ENFILE) {
	    perror("new_connection");
	    return;
	  }
	} 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->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(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) {
    return 0;
#ifdef LOCKOUT
  } else if (forbidden_site(ntohl(addr.sin_addr.s_addr))) {
    fprintf(stderr, "REFUSED CONNECTION from %s(%d) on descriptor %d\n",
	    addrout(addr.sin_addr.s_addr),
	    ntohs(addr.sin_port), newsock);
    shutdown(newsock, 2);
    close(newsock);
    errno = 0;
    return 0;
#endif				/* LOCKOUT */
  } else {
    struct hostent *hent;
    char buff[100];
    long tt;
    hent = gethostbyaddr(&(addr.sin_addr.s_addr),
			 sizeof(addr.sin_addr.s_addr), AF_INET);
    if (hent)
      strcpy(buff, hent->h_name);
    else
      strcpy(buff, inet_ntoa(addr.sin_addr.s_addr));
    time(&tt);
    fprintf(stderr, "USER CONNECT: des: %d host %s time: %s", newsock, buff, ctime(&tt));
    return initializesock(newsock, &addr, buff);
  }
}

const char *addrout(a)
    long a;
{
  /* extern char *inet_ntoa(); */
#ifdef HOST_NAME
  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;
{
  if (d->connected) {
    fprintf(stderr, "DISCONNECT descriptor %d player %s(%d)\n",
	    d->descriptor, db[d->player].name, d->player);
    announce_disconnect(d->player);
  } else {
    fprintf(stderr, "DISCONNECT descriptor %d never connected\n",
	    d->descriptor);
  }
  clearstrings(d);
  shutdown(d->descriptor, 2);
  close(d->descriptor);
  freeqs(d);
  *d->prev = d->next;
  if (d->next)
    d->next->prev = d->prev;
  FREE(d);
  ndescriptors--;
}

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->connected = 0;
  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;
  descriptor_list = d;
  /* fprintf(stderr,"4\n");fflush(stderr); */
  welcome_user(d);
  /* fprintf(stderr,"5\n");fflush(stderr); */
  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("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;
  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);
    exit(4);
  }
  listen(s, 5);
  return s;
}

struct text_block *make_text_block(s, n)
    const 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;
    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;
    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;
{
  int space;
  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;
{
  return queue_write(d, s, strlen(s));
}

int process_output(d)
    struct descriptor_data *d;
{
  struct text_block **qp, *cur;
  int cnt;
  for (qp = &d->output.head; cur = *qp;) {
    cnt = write(d->descriptor, cur->start, cur->nchars);
    if (cnt < 0) {
      /* !!!!!! needs investigation !!!!! */
      /* 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;
{
  queue_string(d, WELCOME_MESSAGE);
}

char *strsave(s)
    const 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;
    const 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;
    const 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)) {
	d->quota--;
	nprocessed++;
	if (!do_command(d, t->start)) {
	  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;
{
  depth = 0;
  if (!strcmp(command, QUIT_COMMAND)) {
    return 0;
  } else if (!strcmp(command, WHO_COMMAND)) {
    if (d->output_prefix) {
      queue_string(d, d->output_prefix);
      queue_write(d, "\n", 1);
    }
    dump_users(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->connected) {
      if (d->output_prefix) {
	queue_string(d, d->output_prefix);
	queue_write(d, "\n", 1);
      }
      cplr = d->player;
      strcpy(ccom, command);
      process_command(d->player, command, d->player);
      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;
    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 (!strncmp(command, "co", 2)) {
    player = connect_player(user, password);
    if (player == NOTHING) {
      queue_string(d, connect_fail);
      fprintf(stderr, "FAILED CONNECT %s on descriptor %d\n",
	      user, d->descriptor);
    } else {
      fprintf(stderr, "CONNECTED %s(%d) on descriptor %d\n",
	      db[player].name, player, d->descriptor);
      d->connected = 1;
      d->connected_at = time(0);
      d->player = player;
      announce_connect(player);
      do_look_around(player);
    }
  } else if (!strncmp(command, "cr", 2)) {
    player = create_player(user, password);
    if (player == NOTHING) {
      queue_string(d, create_fail);
      fprintf(stderr, "FAILED CREATE %s on descriptor %d\n",
	      user, d->descriptor);
    } else {
      fprintf(stderr, "CREATED %s(%d) on descriptor %d\n",
	      db[player].name, player, d->descriptor);
      d->connected = 1;
      d->connected_at = time(0);
      d->player = player;
      announce_connect(player);
      do_look_around(player);
    }
  } else {
    welcome_user(d);
  }
}

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;
  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;
  for (d = descriptor_list; d; d = dnext) {
    dnext = d->next;
    write(d->descriptor, shutdown_message, strlen(shutdown_message));
    if (shutdown(d->descriptor, 2) < 0)
      perror("shutdown");
    close(d->descriptor);
  }
  close(sock);
}

void emergency_shutdown()
{
  close_sockets();
}

void boot_off(player)
    dbref player;
{
  struct descriptor_data *d;
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected && d->player == player) {
      shutdownsock(d);
      return;
    }
  }
}

int bailout(sig, code, scp)
    int sig;
    int code;
    struct sigcontext *scp;
{
  char message[1024];
  sprintf(message, "BAILOUT: caught signal %d code %d", sig, code);
  panic(message);
  _exit(7);
  return 0;
}

int dump_status()
{
  struct descriptor_data *d;
  long now;
  (void) time(&now);
  fprintf(stderr, "STATUS REPORT:\n");
  for (d = descriptor_list; d; d = d->next) {
    if (d->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");
    }
  }
  return 0;
}

void dump_users(e)
    struct descriptor_data *e;
{
  struct descriptor_data *d;
  long now;
  char buf[1024];
  time(&now);
  queue_string(e, "Player Name          On For Idle\n");
  for (d = descriptor_list; d; d = d->next) {
    if (d->connected) {
      sprintf(buf, "%-16s %10s %4s",
	      db[d->player].name,
	      time_format_1(now - d->connected_at),
	      time_format_2(now - d->last_time));
      if (e->connected && Wizard(e->player)) {
	sprintf(buf + strlen(buf),
		" [%s]", d->addr);
      }
      queue_string(e, buf);
      queue_write(e, "\n", 1);
    }
  }
}

const char *time_format_1(dt)
    long dt;
{
#ifdef kjkjkjk
  register 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;
#endif
  return ("");
}

const char *time_format_2(dt)
    long dt;
{
#ifdef klklk
  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;
#endif
  return ("");
}

void announce_connect(player)
    dbref player;
{
  dbref loc;
  char buf[BUFFER_LEN];
  if ((loc = getloc(player)) == NOTHING)
    return;

  sprintf(buf, "%s has connected.", db[player].name);

  notify_except(db[loc].contents, player, buf);
  db[player].flags |= PLAYER_CONNECT;
}

void announce_disconnect(player)
    dbref player;
{
  dbref loc;
  int num;
  char buf[BUFFER_LEN];
  struct descriptor_data *d;
  if ((loc = getloc(player)) == NOTHING)
    return;

  sprintf(buf, "%s has disconnected.", db[player].name);

  notify_except(db[loc].contents, player, buf);
  for (num = 0, d = descriptor_list; d; d = d->next)
    if (d->connected && (d->player == player))
      num++;
  if (num < 2)
    db[player].flags &= ~PLAYER_CONNECT;

}