#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#if defined(_SEQUENT_)
#include <sys/procstats.h>
#endif
#include <fcntl.h>
#if !defined(FNDELAY) && defined(O_NDELAY)
#define	FNDELAY	O_NDELAY
#endif
#if !defined(FASYNC)
#if defined(O_ASYNC)
#define FASYNC O_ASYNC
#else
#define FASYNC 0
#endif
#endif
#define	TELOPTS
#include <arpa/telnet.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <memory.h>
#include <fcntl.h>
#include <setjmp.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include "config.h"
#include "lint.h"
#include "interpret.h"
#include "comm.h"
#include "object.h"
#include "sent.h"
#include "patchlevel.h"

#ifndef EPROTO
#define EPROTO EPROTOTYPE
#endif

int socket (int, int, int);
#if !defined(sgi) && !defined(LINUX) && !defined(__NetBSD__) && !defined(__bsdi__) && !defined(_SEQUENT_)
#ifdef NeXT
int setsockopt (int, int, int, void *, int);
#else
int setsockopt (int, int, int, const char *, int);
#endif
int bind (int, struct sockaddr *, int);
int listen (int, int);
int accept (int, struct sockaddr *, int *);
#endif /* sgi */
int select (int, fd_set *, fd_set *, fd_set *, struct timeval *);

#if !defined(LINUX) && !defined(__NetBSD__) && !defined(__bsdi__)
void bzero (char *, int);
#endif
void telnet_neg (char *, char *);
void set_prompt (char *);
char *query_ip_number (struct object *);
static void add_ip_entry (unsigned long, char *);

extern char *xalloc(), *string_copy(), *unshared_str_copy();
extern int d_flag;
extern int current_time;
extern int eval_cost;
extern struct svalue *sp;
char * first_cmd_in_buf (struct interactive *);
void next_cmd_in_buf (struct interactive *);
char * skip_eols (struct interactive *, char *);
void remove_flush_entry (struct interactive *ip);

void remove_interactive(), add_ref();

extern void debug_message(), fatal(), free_sentence();

struct interactive *all_players[MAX_PLAYERS];

extern int errno;

void new_player();

void flush_all_player_mess();
static struct in_addr	ntohl_addr();

fd_set readfds;
int nfds = 0;
int num_player;

FILE *f_ip_demon = NULL, *f_ip_demon_wr;

static struct object *first_player_for_flush=(struct object *)NULL;

/*
 * Interprocess communication interface to the backend.
 */

static int s, udp_send = -1;
extern int port_number;
#ifdef CATCH_UDP_PORT
extern int udp_port;
static int udp_s;
#endif

#ifdef COMM_STAT
int add_message_calls=0;
int inet_packets=0;
int inet_volume=0;
#endif

#ifdef SERVICE_PORT
extern int service_port;
static int service_s = -1, all_services[MAX_PLAYERS];

static void
new_service(int sock)
{
    int i;

    for (i=0 ; i<MAX_PLAYERS ; i++)
	if (all_services[i] < 0)
	    break;
    if (i < MAX_PLAYERS)
	all_services[i] = sock;
    else {
	write(sock, "ERROR Too many services in use.\n", 32);
	close(sock);
    }
}
#endif /* SERVICE_PORT */

#if (FASYNC || defined(FIOASYNC)) && defined(SIGIO)
void
catch_io(int sig)
{
    extern int volatile interupted;

#if defined(SYSV) || defined(LINUX)
    signal(SIGIO, catch_io);
#endif
    interupted = 1;
}
#endif

void 
prepare_ipc() 
{
    struct sockaddr_in sin;
    struct hostent *hp;
    int tmp;
    char host_name[100];

    if (gethostname(host_name, sizeof host_name) == -1)
    {
        perror("gethostname");
	fatal("Error in gethostname()\n");
    }
    hp = gethostbyname(host_name);
    if (hp == 0) 
    {
	(void)fprintf(stderr, "gethostbyname: unknown host.\n");
	exit(1);
    }
    memset((char *)&sin, '\0', sizeof sin);
    memcpy((char *)&sin.sin_addr, hp->h_addr, hp->h_length);
    sin.sin_port = htons((u_short)port_number);
    sin.sin_family = hp->h_addrtype;
    sin.sin_addr.s_addr = INADDR_ANY;
    s = socket(hp->h_addrtype, SOCK_STREAM, 0);
    if (s == -1) 
    {
	perror("socket");
	abort();
    }
    tmp = 1;
    if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
		    (char *) &tmp, sizeof (tmp)) < 0) 
    {
	perror ("setsockopt");
	exit (1);
    }
    if (bind(s, (struct sockaddr *)&sin, sizeof sin) == -1)
    {
	if (errno == EADDRINUSE)
	{
	    fprintf(stderr, "Socket already bound!\n");
	    debug_message("Socket already bound!\n");
	    exit(errno);
	}
	else
	{
	    perror("bind");
	    abort();
	}
    }
    if (listen(s, 5) == -1)
    {
	perror("listen");
	abort();
    }
    tmp = 1;
#if defined(M_UNIX) || defined(NeXT) || defined(_AIX)
    if (ioctl(s, FIONBIO, &tmp) == -1)
    {
	perror("ioctl socket FIONBIO");
	abort();
    }
#if defined(FIOASYNC) && defined(SIGIO)
    if (ioctl(s, FIOASYNC, &tmp) == -1)
    {
	perror("ioctl socket FIOASYNC");
	abort();
    }
#endif
#else /* M_UNIX */
    fcntl(s, F_SETOWN, getpid());
    if (fcntl(s, F_SETFL, FNDELAY | FASYNC) == -1)
    {
	perror("fcntl socket FNDELAY | FASYNC");
	abort();
    }
#endif /* M_UNIX */
#ifdef SERVICE_PORT
    if (service_port > 0) {
	sin.sin_port = htons((u_short)service_port);
	sin.sin_family = hp->h_addrtype;
	sin.sin_addr.s_addr = INADDR_ANY;
	service_s = socket(hp->h_addrtype, SOCK_STREAM, 0);
	if (service_s >= 0) 
	{
	    tmp = 1;
	    if (setsockopt (service_s, SOL_SOCKET, SO_REUSEADDR,
			    (char *) &tmp, sizeof (tmp)) < 0) 
	    {
		close(service_s);
		service_s = -1;
	    }
	}
	if (service_s >= 0 && bind(service_s, (struct sockaddr *)&sin, sizeof sin) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
	if (service_s >= 0 && listen(service_s, 5) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
#if defined(M_UNIX) || defined(NeXT) || defined(_AIX)
	tmp = 1;
	if (service_s >= 0 && ioctl(service_s, FIONBIO, &tmp) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
#if defined(FIOASYNC) && defined(SIGIO)
	if (service_s >= 0 && ioctl(service_s, FIOASYNC, &tmp) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
#endif
#else /* M_UNIX */
	if (service_s >= 0 && fcntl(service_s, F_SETOWN, getpid()) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
	if (service_s >= 0 && fcntl(service_s, F_SETFL, FNDELAY | FASYNC) == -1)
	{
	    close(service_s);
	    service_s = -1;
	}
#endif /* M_UNIX */
    }
    for (tmp = 0 ; tmp < MAX_PLAYERS ; tmp++)
	all_services[tmp] = -1;
#endif /* SERVICE_PORT */
    signal(SIGPIPE, SIG_IGN);

#ifdef CATCH_UDP_PORT
    if (udp_port != -1)
    {
	sin.sin_port = htons((u_short)udp_port);
#ifdef DEBUG
	if (d_flag & DEBUG_COMMAND)
	    debug_message("UDP recv-socket on port: %d\n", udp_port);
#endif
	udp_s = socket(hp->h_addrtype, SOCK_DGRAM, 0);
	if (udp_s == -1) 
	{
	    perror("udp_socket");
	    abort();
	}
	tmp = 1;
	if (setsockopt (udp_s, SOL_SOCKET, SO_REUSEADDR,
			(char *) &tmp, sizeof (tmp)) < 0) 
	{
	    perror ("setsockopt");
	    exit (1);
	}
	if (bind(udp_s, (struct sockaddr *)&sin, sizeof sin) == -1) 
	{
	    if (errno == EADDRINUSE) 
	    {
		fprintf(stderr, "UDP Socket already bound!\n");
		debug_message("UDP Socket already bound!\n");
		close(udp_s);
		udp_port = -1;
	    } 
	    else 
	    {
		perror("udp-bind");
		abort();
	    }
	}
    }
    if (udp_port != -1)
    {
	tmp = 1;
#if defined(M_UNIX) || defined(NeXT) || defined(_AIX)
	if (ioctl(udp_s, FIONBIO, &tmp) == -1) 
	{
	    perror("udp - ioctl socket FIONBIO");
	    abort();
	}
#if defined(FIOASYNC) && defined(SIGIO)
	if (ioctl(udp_s, FIOASYNC, &tmp) == -1) 
	{
	    perror("udp - ioctl socket FIOASYNC");
	    abort();
	}
#endif
#else /* M_UNIX */
	if (udp_s >= 0 && fcntl(udp_s, F_SETOWN, getpid()) == -1)
	{
	    perror("udp - fcntl socket F_SETOWN");
	    abort();
	}
	if (fcntl(udp_s, F_SETFL, FNDELAY | FASYNC) == -1) 
	{
	    perror("udp - fcntl socket FNDELAY | FASYNC");
	    abort();
	}
#endif /* M_UNIX */
    }
#endif /* CATCH_UDP_PORT */

}

/*
 * This one is called when shutting down the MUD.
 */
void 
ipc_remove() 
{
    (void)printf("Shutting down ipc...\n");
    if (udp_send >= 0)
	close(udp_send);
    close(s);
}
void write_socket();

/*
 * Send a message to a player. If that player is shadowed, special
 * care has to be taken.
 */
/*VARARGS1*/

void add_message2(ob,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
    char *fmt;
    long a1, a2, a3, a4, a5, a6, a7, a8, a9;
    struct object *ob;
{
    char buff[10000];
    sprintf(buff,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
    if (ob && !(ob->flags & O_DESTRUCTED))
	{
	    push_string(buff,STRING_MALLOC);
	    apply("catch_tell", ob, 1, 1);
	}
    else
	write_socket(buff, (struct object *)0);
    
}

void
add_message(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
    char *fmt;
    long a1, a2, a3, a4, a5, a6, a7, a8, a9;
{
    add_message2(command_giver,fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
}

void 
write_socket(char *fmt, struct object *command_giver)
{
    char buff[10000];		/* Kludgy! Hope this is enough ! */
    char buff2[MAX_SOCKET_PACKET_SIZE+1];
    char *c_from, *c_to;
    struct interactive *ip;
    int n, chunk, length;
    int from, to;
    int min_length;
    int old_message_length;

    if (command_giver == 0 || (command_giver->flags & O_DESTRUCTED) ||
	command_giver->interactive == 0 ||
	command_giver->interactive->do_close) {
	if ( fmt != MESSAGE_FLUSH )
	    fputs(fmt, stderr);
	fflush(stderr);
	return;
    }


    ip = command_giver->interactive;
    old_message_length = ip->message_length;
    if ( fmt == MESSAGE_FLUSH ) {
	min_length = 1;
	strncpy ( buff, ip->message_buf, length=old_message_length );
    	buff[length] = '\0';
    }
    else
    {
#ifdef COMM_STAT
	add_message_calls++; /* We want to know how many packets the old
				version would have sent			 */
#endif
	min_length = DESIRED_SOCKET_PACKET_SIZE;
	if (old_message_length + strlen(fmt) > sizeof buff)
	    error("Too long message!\n");
	(void) strcpy(buff + old_message_length, fmt);
	length = old_message_length + strlen(buff + old_message_length);
	/*
	 * Always check that your arrays are big enough ! 
	 */
	if (shadow_catch_message(command_giver, buff + old_message_length))
	    return;
	/* Insert super-snooper here */
#ifdef SUPER_SNOOP
	{ /* This is *crude* code  -Azid */
	    extern char super_snooped[256][16], super_snoopfile[256][32];
	    extern int num_super_snooped;
	    extern struct svalue *sapply();
	    if (num_super_snooped)
	    {
		int i, f;
		for (i = 0; i < num_super_snooped; i++)
		{
		    if (ip->ob->living_name != NULL)
		    {
			if (strcmp(ip->ob->living_name, super_snooped[i]) == 0)
			{
			    f = open(super_snoopfile[i], O_WRONLY | O_APPEND | O_CREAT, 0600);
			    if (f != NULL)
			    {
				write(f, buff + old_message_length,
				      strlen(buff + old_message_length));
				close(f);
			    }
			    break;
			}
		    }
		}
	    }
	}
	
	/* End of super-snooper */
#endif
	if (ip->snoop_by)
	    add_message2(ip->snoop_by->ob,"%%%s", buff + old_message_length);
	if ( length >= min_length )
	    strncpy ( buff, ip->message_buf, old_message_length );
	else
	    strcpy(ip->message_buf + old_message_length,
		   buff + old_message_length );
    }
    if (d_flag & DEBUG_OUTPUT)
	debug_message("[%s(%d)]: %s", command_giver->name, length, buff);
    /*
     * Insert CR after all NL.
     */
    to = 0;
    if (ip->current_column > ip->screen_width)
	ip->current_column = ip->screen_width;

    for (from = 0 ; length - from >= min_length ; to = 0 )
    {
#ifdef WORD_WRAP
	if (ip->screen_width > 3)
	{
	    int i;
	    int fold_size;

	    fold_size = ip->screen_width / 5;

	    while(to < sizeof(buff2) - 4 && buff[from] != '\0')
	    {
		if (buff[from] == '\n') 
		{
		    buff2[to++] = '\r';
		    buff2[to++] = '\n';
		    from++;
		    ip->current_column = 0;
		    continue;
		}
		if (buff[from] == '\t')
		{
		    ip->current_column &= ~7;
		    ip->current_column += 8;
		    if (ip->current_column >= ip->screen_width)
		    {
			buff2[to++] = '\r';
			buff2[to++] = '\n';
			ip->current_column = 0;

			/* Skip whitespace at the beginning of the line */
			while (buff[from] == ' ' || buff[from] == '\t')
			    from++;

			if (buff[from] == '\n')
			    from++;
		    }
		    else
			buff2[to++] = '\t';
		    from++;
		    continue;
		}

		for (i = 1; buff[from + i] >= '!'; i++);

		if (i + ip->current_column >= ip->screen_width)
		    if ( ip->current_column > ip->screen_width - fold_size)
		    {
			buff2[to++] = '\r';
			buff2[to++] = '\n';
			ip->current_column = 0;

			/* Skip whitespace at the beginning of the line */
			while (buff[from] == ' ' || buff[from] == '\t')
			    from++;

			if (buff[from] == '\n')
			    from++;
		    }
		    else
		    {
			if (to + ip->screen_width - ip->current_column + 2 >= 
			    sizeof(buff2) - 4)
			    break;
			
			memcpy(&buff2[to], &buff[from],
			      ip->screen_width - ip->current_column - 1);
			from += ip->screen_width - ip->current_column - 1;
			to += ip->screen_width - ip->current_column - 1;
			buff2[to++] = '-';
			buff2[to++] = '\r';
			buff2[to++] = '\n';

			ip->current_column = 0;
		    }
		else
		{
		    if (to + i >= sizeof(buff2) - 4)
			break;
		    memcpy(&buff2[to], &buff[from], i);
		    from += i;
		    to += i;
		    ip->current_column += i;
		}
		
	    }
	}
	else
#endif /* WORD_WRAP */
	{
	    
	    for ( ; to < (sizeof buff2)-1 && buff[from] != '\0';)
	    {
		if (buff[from] == '\n')
		    buff2[to++] = '\r';
		buff2[to++] = buff[from++];
	    }
	}
	chunk = to;
    /*
     * We split up the message into something smaller than the max size.
     */
	if ((n = write(ip->socket, buff2, chunk)) == -1)
	{
	    if (errno == EMSGSIZE)
		return;
	    if (errno == EINVAL)
	    {
	        if (old_message_length) remove_flush_entry(ip);
		ip->do_close = 1;
		return;
	    }
	    if (errno == ENETUNREACH)
	    {
	        if (old_message_length) remove_flush_entry(ip);
		ip->do_close = 1;
		return;
	    }
	    if (errno == EHOSTUNREACH)
	    {
	        if (old_message_length) remove_flush_entry(ip);
		ip->do_close = 1;
		return;
	    }
	    if (errno == EPIPE) {
	        if (old_message_length) remove_flush_entry(ip);
		ip->do_close = 1;
		return;
	    }
	    if (errno == EWOULDBLOCK) {
	        if (old_message_length)
		    remove_flush_entry(ip);
/*		ip->do_close = 1;  -- LA */
		return;
	    }
	    fprintf(stderr, "write: unknown errno %d\n", errno);
	    perror("write");
	    if (old_message_length) remove_flush_entry(ip);
	    ip->do_close = 1;
	    return;
	}
#ifdef COMM_STAT
	inet_packets++;
	inet_volume += n;
#endif
	if (n != chunk)
	    fprintf(stderr, "write socket: wrote %d, should be %d.\n",
		    n, chunk);
	continue;
    }
    length -= from;
    ip->message_length = length;
    if (from)
        strncpy( ip->message_buf, buff + from, length );
    if ( length && !old_message_length )
    { /* buffer became 'dirty' */
	if ( ip->next_player_for_flush = first_player_for_flush )
	{
	    first_player_for_flush->interactive->previous_player_for_flush =
		command_giver;
	}
	ip->previous_player_for_flush = 0;
	first_player_for_flush = command_giver;
    }
    if ( !length && old_message_length )  /* buffer has become empty */
	remove_flush_entry(ip);
}

void
remove_flush_entry(struct interactive *ip)
{

    ip->message_length = 0;
    if ( ip->previous_player_for_flush )
    {
	ip->previous_player_for_flush->interactive->next_player_for_flush
	= ip->next_player_for_flush;
    }
    else
    {
	first_player_for_flush = ip->next_player_for_flush;
    }
    if ( ip->next_player_for_flush )
    {
	ip->next_player_for_flush->interactive->previous_player_for_flush
	= ip->previous_player_for_flush;
    }
}

void 
flush_all_player_mess()
{
    struct object *p,*np;

    for ( p = first_player_for_flush; p; p = np)
    {
        np = p->interactive->next_player_for_flush;
	/* beware of side-effects when calling add_message the first time! */
	write_socket(MESSAGE_FLUSH,p);
    }
}


/*
 * Copy a string, replacing newlines with '\0'. Also add an extra
 * space and back space for every newline. This trick will allow
 * otherwise empty lines, as multiple newlines would be replaced by
 * multiple zeroes only.
 */
static INLINE int 
copy_chars(char *from, char *to, int n)
{
    int i;
    char *start = to;
    for (i = 0; i < n; i++)
    {
	if (from[i] == '\r')
	    continue;
	if (from[i] == '\n')
	{
	    *to++ = ' ';
	    *to++ = '\b';
	    *to++ = '\0';
	    continue;
	}
	*to++ = from[i];
    }
    return to - start;
}

/*
 * Get a message from any player.  For all players without a completed
 * cmd in their input buffer, read their socket.  Then, return the first
 * cmd of the next player in sequence that has a complete cmd in their buffer.
 * CmdsGiven is used to allow people in ED to send more cmds (if they have
 * them queued up) than normal players.  If we get a heartbeat, still read
 * all sockets; if the next cmd giver is -1, we have already cycled and
 * can go back to do the heart beat.
 */

#define	StartCmdGiver	(MAX_PLAYERS-1) /* the one after heartbeat */
#define IncCmdGiver	NextCmdGiver = (NextCmdGiver < 0? StartCmdGiver: \
					NextCmdGiver - 1)

int NextCmdGiver = StartCmdGiver;
int CmdsGiven = 0;	 /* -1 is used to poll heart beat. */
int twait = 0;		 /* wait time for select() */
extern comm_time_to_call_heart_beat;

#define MSR_SIZE 20

int	msecs_response[MSR_SIZE];
int	msr_point		= -1;
long	last_sec, last_usec;

int 
get_msecs_response(int ix)
{
    return (ix >= 0 && ix < MSR_SIZE) ? msecs_response[ix] : -1;
}

int 
get_message(char *buff, int size)
{
    int i, res;
    struct interactive *ip = 0;
    char *p;

    /*
     * Stay in this loop until we have a message from a player.
     */
    while(1)
    {
	int new_socket, cnt;
	struct sockaddr_in addr;
	int length;
	struct timeval timeout;
#ifdef CATCH_UDP_PORT
	char udp_buf[1024], *st;
#endif
#ifdef SERVICE_PORT
	char service_buf[1024];
	int service;
#endif /* SERVICE_PORT */
	/* First, try to get a new player... */
	length = sizeof addr;
	new_socket = accept(s, (struct sockaddr *)&addr, &length);
	if (new_socket != -1)
	    {
#if defined(M_UNIX) || defined(NeXT) || defined(_AIX)
		i = 1;
		ioctl(new_socket, FIONBIO, &i);
#else
		fcntl(new_socket, F_SETOWN, getpid());
		fcntl(new_socket, F_SETFL, FNDELAY | FASYNC);
#endif
		new_player(new_socket, &addr, length);
	    }
	else if (new_socket == -1 && errno != EWOULDBLOCK && errno != EINTR &&
		 errno != EPROTO)
	{
	    perror("accept");
	    abort();
	}
#ifdef SERVICE_PORT
	/* Next, try to get a new service... */
	if (service_s > -1)
	{
	    length = sizeof addr;
	    new_socket = accept(service_s, (struct sockaddr *)&addr, &length);
	    if (new_socket != -1)
	    {
#if defined(M_UNIX) || defined(NeXT) || defined(_AIX)
		i = 1;
		ioctl(new_socket, FIONBIO, &i);
#else
		fcntl(new_socket, F_SETOWN, getpid());
		fcntl(new_socket, F_SETFL, FNDELAY | FASYNC);
#endif
		new_service(new_socket);
	    }
	    else if (new_socket == -1 && errno != EWOULDBLOCK &&
		     errno != EINTR && errno != EPROTO)
	    {
		perror("accept");
		abort();
	    }
	}
#endif /* SERVICE_PORT */
#ifdef CATCH_UDP_PORT
	/* Then see if we got any udp messages */
	if (udp_port != -1)
	{
	    cnt = recvfrom(udp_s, udp_buf, sizeof(udp_buf), 0, 
			   (struct sockaddr *)&addr, &length);
	    if (cnt != -1)
	    {
		udp_buf[sizeof(udp_buf) - 1] = 0;
		udp_buf[cnt] = 0;
		st = inet_ntoa(addr.sin_addr);
		push_string(st, STRING_MALLOC);
		push_string(udp_buf, STRING_MALLOC);
		apply_master_ob(M_INCOMING_UDP, 2);
	    }
	}
#endif

	nfds = 0;
	FD_ZERO(&readfds);
	for (i = 0; i < MAX_PLAYERS; i++)
	{
	    ip = all_players[i];
	    if (!ip)
		continue;
	    if (ip->do_close)
	    {
		ip->do_close = 0;
		remove_interactive(ip->ob, 1);
		continue;
	    }
	    if (!first_cmd_in_buf(ip))
	    {
		FD_SET(ip->socket, &readfds);
		if (ip->socket >= nfds)
		    nfds = ip->socket+1;
	    }
	}
#ifdef SERVICE_PORT
	for (i = 0 ; i < MAX_PLAYERS ; i++)
	{
	    if ((service = all_services[i]) < 0)
		continue;
	    FD_SET(service, &readfds);
	    if (service >= nfds)
		nfds = service + 1;
	}
#endif /* SERVICE_PORT */
	if (f_ip_demon != NULL)
	{
	    FD_SET(fileno(f_ip_demon), &readfds);
	}
	/*
	    Measure average response time
	 */
#if defined(_SEQUENT_)
	get_process_stats(&timeout, PS_SELF, NULL, NULL);
#else
	gettimeofday(&timeout, 0);
#endif
	if (msr_point < 0)
	{
	    for (i = 0; i < MSR_SIZE; i++)
		msecs_response[i] = -1;
	    msr_point = 0;
	}
	else
	{
	    msecs_response[msr_point++] = (timeout.tv_sec - last_sec) * 1000 +
		((timeout.tv_usec > last_usec)
		    ? (timeout.tv_usec - last_usec) / 1000
		    : (1000000 - (last_usec - timeout.tv_usec)) / 1000);

	    /* Do not store the zero values */
	    if (msecs_response[msr_point - 1] < 5)
		msr_point--;
	    msr_point %= MSR_SIZE;
	}
	timeout.tv_sec = 0; /* avoid busy waiting when no buffered cmds */
	timeout.tv_usec = 0;
	res = select(nfds, &readfds, 0, 0, &timeout);

#if defined(_SEQUENT_)
	get_process_stats(&timeout, PS_SELF, NULL, NULL);
#else
	gettimeofday(&timeout, 0);
#endif
	last_sec = timeout.tv_sec;
	last_usec = timeout.tv_usec;
	if (res == -1)
	{
	    twait = 0;
	    if (errno == EINTR) /* if we got an alarm, finish the round */
		goto return_next_command;
	    perror("select");
	    return 0;
	}
	if (res)
	{ /* waiting packets */
	    if (f_ip_demon != NULL && FD_ISSET(fileno(f_ip_demon), &readfds))
	    {
		char buf[200], *pp, *q;
		unsigned long laddr;
		if (fgets(buf, sizeof buf, f_ip_demon))
		{
/*printf("hname says: %s\n", buf);*/
		    laddr = inet_addr(buf);
		    if (laddr != -1)
		    {
			pp = strchr(buf, ' ');
			if (pp)
			{
			    pp++;
			    q = strchr(buf, ',');
			    if (q)
			    {
				char *p1, *p2, *name;
				*q = 0;
				p1 = q + 1;
				if (p2 = strchr(p1, ','));
				{
				    *p2 = 0;
				    p2++;
				    if(name = strchr(p2, ':'))
				    {
					*name = 0;
					name++;
					if(q = strchr(name, '\n'))
					{
					    int i;
					    struct interactive *ir;
					    int lport, rport;
					    
					    *q = 0;
					    lport = atoi(p1);
					    rport = atoi(p2);
					    for (i = 0; i < MAX_PLAYERS; i++)
					    {
						if (!(ir = all_players[i]))
						    continue;
						if (ir->addr.sin_addr.s_addr
						    == laddr &&
						    ir->lport == lport &&
						    ir->rport == rport)
						{
						    ir->rname =
							xalloc(strlen(name) +
							       1);
						    strcpy(ir->rname, name);
						    break;
						}
						
					    }
					}
				    }
				}
			    }
			    else
				q = strchr(buf, '\n');
			    
			    if (q)
			    {
				*q = 0;
				add_ip_entry(laddr, pp);
			    }
			}
		    }
		}
	    }
#ifdef SERVICE_PORT
	    for (i = 0; i < MAX_PLAYERS; i++)
	    {
		int l;
		struct svalue *ret;

		service = all_services[i];
		if (service < 0)
		    continue;
		if (FD_ISSET(service, &readfds)) {
		    if ((l = read(service, service_buf, sizeof(service_buf)-1)) == -1) {
			close(service);
			all_services[i] = -1;
			continue;
		    }
		    service_buf[l] = '\0';
		    push_string(service_buf, STRING_MALLOC);
		    ret = apply_master_ob(M_INCOMING_SERVICE, 1);
		    if (ret == 0 || ret->type != T_STRING) {
			write(service, "ERROR Service calls not supported\n", 34);
			close(service);
			all_services[i] = -1;
			continue;
		    }
		    if (write(service, ret->u.string, strlen(ret->u.string)) < 0) {
			close(service);
			all_services[i] = -1;
			continue;
		    }
		}
	    }
#endif /* SERVICE_PORT */
	    for (i = 0; i < MAX_PLAYERS; i++)
	    { /* read all pending sockets */
		ip = all_players[i];
		if (ip == 0)
		    continue;
		if (FD_ISSET(ip->socket, &readfds))
		{ /* read this player */
		    int l;

		    /*
		    * Dont overfill their buffer.
		     * Use a very conservative estimate on how much we can
		     * read.
		     */
		    l = (MAX_TEXT - ip->text_end - 1) / 3;
		    if (l < size)
			size = l;

		    if ((l = read(ip->socket, buff, size)) == -1)
		    {
			if (errno == ENETUNREACH)
			{
			    debug_message("Net unreachable detected.\n");
			    remove_interactive(ip->ob, 1);
			    continue;
			}
			if (errno == EHOSTUNREACH)
			{
			    debug_message("Host unreachable detected.\n");
			    remove_interactive(ip->ob, 1);
			    continue;
			}
			if (errno == ETIMEDOUT)
			{
			    debug_message("Connection timed out detected.\n");
			    remove_interactive(ip->ob, 1);
			    continue;
			}
			if (errno == ECONNRESET)
			{
			    debug_message("Connection reset by peer detected.\n");
			    remove_interactive(ip->ob, 1);
			    continue;
			}
			if (errno == EWOULDBLOCK)
			{
			    debug_message("read would block socket %d!\n",
					  ip->socket);
			    remove_interactive(ip->ob, 1);
			    continue;
			}
			if (errno == EMSGSIZE)
			{
			    debug_message("read EMSGSIZE !\n");
			    continue;
			}
			perror("read");
			debug_message("Unknown errno %d\n", errno);
			remove_interactive(ip->ob, 1);
			continue;
		    }
		    if (l == 0)
		    {
			if (ip->closing)
			    fatal("Tried to read from closing socket.\n");
			remove_interactive(ip->ob, 1);
			return 0;
		    }
		    buff[l] = '\0';
		    /* replace newlines by nulls and catenate to buffer */
		    ip->text_end +=
			copy_chars(buff, ip->text + ip->text_end, l);
		    /* now, text->end is just after the last char read.  If last */
		    /* char was a nl, char *before* text_end will be null. */
		    ip->text[ip->text_end] = '\0';
		}
	    }
	}
	/*
	 * we have read the sockets, now find and return a command
	 */
        return_next_command:
	twait = 0;
	ip = 0;
	for (i = 0; i < MAX_PLAYERS + 1; i++)
	{
	    if (NextCmdGiver == -1)
	    { /* we have cycled around all players */
		CmdsGiven = 0;	  /* check heart beat */
		IncCmdGiver;
		return 0;
	    }
	    ip = all_players[NextCmdGiver];
	    if (ip && (p = first_cmd_in_buf(ip))) /* wont respond to partials*/
		break;
	    CmdsGiven = 0; /* new player, no cmds issued */
	    IncCmdGiver;
	}
	
	if (!ip || !p)
	{ /* no cmds found; loop and select (on timeout) again */
	    NextCmdGiver = StartCmdGiver;/* do a complete poll next time */
	    CmdsGiven = 0;
	    return(0);
	}
	
	/*
	 * we have a player cmd - return it.  If he is in ed, count his
	 * cmds, else only allow 1 cmd.  If he has only one partially
	 * completed cmd left after * this, move it to the start of his
	 * buffer; new stuff will be appended.
	 */
	
	command_giver = ip->ob;
	telnet_neg(buff, p);
	next_cmd_in_buf(ip); /* move on buffer pointers */
		    ip->current_column = 0;
	
	/* if he is not in ed, dont let him issue another till the poll comes again */
	
	if (ip->ed_buffer && CmdsGiven < ALLOWED_ED_CMDS)
	    CmdsGiven++;
	else {
	    IncCmdGiver;
	    CmdsGiven = 0;
	}
	
	/* manage snooping - should the snooper see type ahead?
	    Well, he doesnt here
	 */

	if (!ip->noecho)
	{ /* This is *crude* code  -Azid */
	    extern char super_snooped[256][16], super_snoopfile[256][32];
	    extern int num_super_snooped;
	    extern struct svalue *sapply();
	    
#ifdef SUPER_SNOOP
	    if (num_super_snooped)
	    {
		int i, f;
		for (i = 0; i < num_super_snooped; i++)
		{
		    if (ip->ob->living_name != NULL)
		    {
			if (strcmp(ip->ob->living_name, super_snooped[i]) == 0) {
			    f = open(super_snoopfile[i], O_WRONLY | O_APPEND | O_CREAT, 0600);
			    if (f != NULL)
			    {
				write(f, buff, strlen(buff));
				write(f,"\n",1);
				close(f);
			    }
			    break;
			}
		    }
		}
	    }
#endif	
	}
	if (ip->snoop_by && !ip->noecho) 
	    add_message2(ip->snoop_by->ob,"%% %s\n", buff);
	command_giver = ip->ob;
	if (ip->noecho)
	{
	    /* Must not enable echo before the user input is received. */
	    add_message("%c%c%c", IAC, WONT, TELOPT_ECHO);
	}
	ip->noecho = 0;
	ip->last_time = current_time;
	return 1;
    }
}

/*
 * find the first character of the next complete cmd in a buffer, 0 if no
 * completed cmd.  There is a completed cmd if there is a null between
 * text_start and text_end.  Zero length commands are discarded (as occur
 * between <cr> and <lf>).  Update text_start if we have to skip leading
 * nulls.
 */

char * 
first_cmd_in_buf(struct interactive *ip) 
{
    char * p, *q;

    p = ip->text_start + ip->text;

    while ((p < (ip->text_end + ip->text)) && !*p) /* skip null input */
	p++;

    ip->text_start = p - ip->text;

    if (ip->text_start >= ip->text_end)
    {
	ip->text_start = ip->text_end = 0;
	ip->text[0] = '\0';
	return(0);
    }

    while ((p < (ip->text_end + ip->text)) && *p) /* find end of cmd */
	p++;

    if (p < ip->text + ip->text_end) /* null terminated, was command */
	return(ip->text + ip->text_start);

/* have a partial command at end of buffer; move it to start, return null */
/* if it cant move down, truncate it and return it as cmd. */
    
    p = ip->text + ip->text_start;
    q = ip->text;
    while (p < (ip->text + ip->text_end))
	*(q++) = *(p++);

    ip->text_end -= ip->text_start;
    ip->text_start = 0;
    if (ip->text_end > MAX_TEXT - 2)
    {
	ip->text[ip->text_end-2] = '\0'; /* nulls to truncate */
	ip->text[ip->text_end-1] = '\0'; /* nulls to truncate */
	ip->text_end--;
	return(ip->text);
	}
/* buffer not full and no newline - no cmd. */
    return(0);
}

/*
 * move pointers to next cmd, or clear buf.
 */

void
next_cmd_in_buf(struct interactive *ip)
{
    char * p = ip->text + ip->text_start;
    while (*p && p < ip->text + ip->text_end)
	p++;
    /* skip past any nulls at the end */
    while (!*p && p < ip->text + ip->text_end)
	p++;
    if (p < ip->text + ip->text_end)
	ip->text_start = p - ip->text;
    else
    {
	ip->text_start = ip->text_end = 0;
	ip->text[0] = '\0';
    }
}
    

/*
 * Remove an interactive player immediately.
 */
void
remove_interactive(struct object *ob, int link_dead)
{
    struct object *save = command_giver;
    int i;

    if (!ob || !(ob->interactive))
	return;
    for (i = 0; i < MAX_PLAYERS; i++)
    {
	if (all_players[i] != ob->interactive)
	    continue;
	if (ob->interactive->closing)
	    fatal("Double call to remove_interactive()\n");

	command_giver = ob;

	if (ob->interactive->ed_buffer)
	{
	    extern void save_ed_buffer();

	    /* This will call 'get_ed_buffer_save_file_name' in master_ob
             * If that fails(error in LPC) the closing IP will hang and
	     * on the next read a 'fatal read on closing socket will occur'
	     * unless we do this here before ip->closing = 1
	     */
	    add_message("Saving editor buffer.\n");
	    save_ed_buffer();
	}
	if (!(ob->interactive))
	    return;
	ob->interactive->closing = 1;
	if (ob->interactive->snoop_by)
	{
	    ob->interactive->snoop_by->snoop_on = 0;
	    ob->interactive->snoop_by = 0;
	}
	if (ob->interactive->snoop_on)
	{
	    ob->interactive->snoop_on->snoop_by = 0;
	    ob->interactive->snoop_on = 0;
	}
	write_socket("Closing down.\n", command_giver);
	write_socket(MESSAGE_FLUSH, command_giver);
	(void)shutdown(ob->interactive->socket, 2);
	close(ob->interactive->socket);
	num_player--;
	if (ob->interactive->input_to)
	{
	    free_vector(ob->interactive->carryover);
	    free_sentence(ob->interactive->input_to);
	    ob->interactive->input_to = 0;
	}
	if(ob->interactive->rname)
	    free(ob->interactive->rname);
	
	free((char *)ob->interactive);
	ob->interactive = 0;
	all_players[i] = 0;
	free_object(ob, "remove_interactive");

	push_object(ob);
	push_number(link_dead);
	apply_master_ob(M_REMOVE_INTERACTIVE,2);
	
	command_giver = save;
	return;
    }
    (void) fprintf(stderr, "Could not find and remove player %s\n", ob->name);
    abort();
}


/*
 * get the I'th player object from the interactive list, i starts at 0
 * and can go to num_player - 1.  For users(), etc.
 */
struct object * 
get_interactive_object(i)
int i;
{
    int n;

    if (i >= num_player) /* love them ASSERTS() :-) */
	fatal("Get interactive (%d) with only %d players!", i, num_player);

    for (n = 0; n < MAX_PLAYERS; n++)
	if (all_players[n])
	    if (!(i--))
		return(all_players[n]->ob);

    fatal("Get interactive: player %d not found! (num_players = %d)",
		i, num_player);
    return 0;	/* Just to satisfy some compiler warnings */
}

void 
new_player(int new_socket, struct sockaddr_in *addr, int len)
{
    int i;
    char *p;
   
    if (d_flag & DEBUG_CONNECT)
	debug_message("New player at socket %d.\n", new_socket);
    for (i = 0; i < MAX_PLAYERS; i++)
    {
	struct object *ob;
	struct svalue *ret;
	extern struct object *master_ob;
	
	if (all_players[i] != 0)
	    continue;
	command_giver = master_ob;
	master_ob->interactive =
	    (struct interactive *)xalloc(sizeof (struct interactive));
	master_ob->interactive->default_err_message = 0;
	master_ob->flags |= O_ONCE_INTERACTIVE;
	/* This initialization is not pretty. */
	master_ob->interactive->rname = 0;
	master_ob->interactive->ob = master_ob;
	master_ob->interactive->text[0] = '\0';
	master_ob->interactive->input_to = 0;
	master_ob->interactive->closing = 0;
	master_ob->interactive->snoop_on = 0;
	master_ob->interactive->snoop_by = 0;
	master_ob->interactive->text_end = 0;
	master_ob->interactive->text_start = 0;
	master_ob->interactive->do_close = 0;
	master_ob->interactive->noecho = 0;
	master_ob->interactive->trace_level = 0;
	master_ob->interactive->trace_prefix = 0;
	master_ob->interactive->last_time = current_time;
	master_ob->interactive->ed_buffer = 0;
	master_ob->interactive->message_length=0;
	master_ob->interactive->carryover = allocate_array(0);
#ifdef WORD_WRAP
	master_ob->interactive->current_column = 0;
	master_ob->interactive->screen_width = 0;
#endif
	all_players[i] = master_ob->interactive;
	all_players[i]->socket = new_socket;
	set_prompt(NULL);
#if 1
	memcpy((char *)&all_players[i]->addr, (char *)addr, len);
#else
	getpeername(new_socket, (struct sockaddr *)&all_players[i]->addr,
		    &len);
#endif
	all_players[i]->rport = ntohs(addr->sin_port);
	all_players[i]->lport = port_number;
	num_player++;
	/*
	 * The player object has one extra reference.
	 * It is asserted that the master_ob is loaded.
	 */
	add_ref(master_ob, "new_player");
	ret = apply_master_ob(M_CONNECT, 0);
	if (ret == 0 || ret->type != T_OBJECT)
	{
	    remove_interactive(master_ob, 0);
	    return;
	}
	/*
	 * There was an object returned from connect(). Use this as the
	 * player object.
	 */
	ob = ret->u.ob;
	ob->interactive = master_ob->interactive;
	ob->interactive->ob = ob;
	ob->flags |= O_ONCE_INTERACTIVE;
	master_ob->flags &= ~O_ONCE_INTERACTIVE;
	write_socket(MESSAGE_FLUSH, command_giver);
	master_ob->interactive = 0;
	free_object(master_ob, "reconnect");
	add_ref(ob, "new_player");
	command_giver = ob;
        if (f_ip_demon_wr != NULL) {
/*printf("sent hname %s\n:", query_ip_number(ob));*/
            fprintf(f_ip_demon_wr, "%s;%d,%d\n", query_ip_number(ob),
		    all_players[i]->lport, all_players[i]->rport);
            fflush(f_ip_demon_wr);
        }
	logon(ob);
	flush_all_player_mess();
	return;
    }
    p = "Lpmud is full. Come back later.\r\n";
    write(new_socket, p, strlen(p));
    close(new_socket);
}

int 
call_function_interactive(struct interactive *i, char *str)
{
    char *function;
    struct object *ob;
    int j;
    
    if (!i->input_to)
	return 0;
    /*
     * Special feature: input_to() has been called to setup
     * a call to a function.
     */
    if (i->input_to->ob->flags & O_DESTRUCTED)
    {
	/* Sorry, the object has selfdestructed ! */
	free_sentence(i->input_to);
	i->input_to = 0;
	free_vector(i->carryover);
	i->carryover = allocate_array(0);
	return 0;
    }
    function = string_copy(command_giver->interactive->input_to->function);
    ob = i->input_to->ob;
    free_sentence(i->input_to);
    /*
     * We must clear this reference before the call to apply(), because
     * someone might want to set up a new input_to().
     */
    i->input_to = 0;
    /*
     * Now we set current_object to this object, so that input_to will
     * work for static functions.
     */
    push_string(str, STRING_MALLOC);
    current_object = ob;
    for (j = 0; j < i->carryover->size; j++)
	push_svalue(&(i->carryover->item[j]));
    free_vector(i->carryover);
    i->carryover = allocate_array(0);
    (void)apply(function, ob, 1 + j, 0);
    free(function);
    flush_all_player_mess();
    return 1;
}

int
set_call(struct object *ob, struct sentence *sent, int noecho)
{
    struct object *save = command_giver;
    if (ob == 0 || sent == 0)
	return 0;
    if (ob->interactive == 0 || ob->interactive->input_to)
	return 0;
    ob->interactive->input_to = sent;
    ob->interactive->noecho = noecho;
    command_giver = ob;
    if (noecho)
	add_message("%c%c%c", IAC, WILL, TELOPT_ECHO);
    command_giver = save;
    return 1;
}

static struct in_addr
ntohl_addr(struct in_addr addr)
{
	struct in_addr	ret;

	ret.s_addr=ntohl(addr.s_addr);
	return(ret);
}

void
remove_all_players()
{
    int i;
    struct svalue *ret;
    extern jmp_buf error_recovery_context;
    extern int error_recovery_context_exists;
    
    ret = apply_master_ob(M_START_SHUTDOWN, 0);
    if (ret && ret->type == T_POINTER)
    {
	int i;
	struct vector *unload_vec = ret->u.vec;
	
	unload_vec->ref++;
	
	for (i = 0; i < unload_vec->size; i++)
	    if (setjmp(error_recovery_context)) 
	    {
		clear_state();
		add_message("Anomaly in the fabric of world space.\n");
	    }
	    else
	    {
		error_recovery_context_exists = 1;
		push_svalue(&(unload_vec->item[i]));
		eval_cost = 0;
		apply_master_ob(M_CLEANUP_SHUTDOWN, 1);
	    }
	free_vector(unload_vec);
    }
    error_recovery_context_exists = 0;
    apply_master_ob(M_FINAL_SHUTDOWN, 0);
}

void
set_prompt(char *str)
{
    if (str)
	command_giver->interactive->prompt = str;
    else
	command_giver->interactive->prompt = "> ";
}

/*
 * Print the prompt, but only if input_to not is disabled.
 */
void
print_prompt()
{
    if (command_giver == 0)
	fatal("command_giver == 0.\n");
    if (command_giver->interactive->input_to == 0)
    {
	if (!(command_giver->flags & O_DESTRUCTED) &&
	    command_giver->interactive->prompt)
	    add_message(command_giver->interactive->prompt);
	if (1)
	{ /* add test for heart_beat later */
    	    flush_all_player_mess();
	}
    }
}


/*
 * Let object 'me' snoop object 'you'. If 'you' is 0, then turn off
 * snooping.
 *
 * This routine is almost identical to the old set_snoop. The main
 * difference is that the routine writes nothing to player directly,
 * all such communication is taken care of by the mudlib. It communicates
 * with master.c in order to find out if the operation is permissble or
 * not. The old routine let everyone snoop anyone. This routine also returns
 * 0 or 1 depending on success.
 */
int
set_snoop(struct object *me, struct object *you)
{
    struct interactive *on = 0, *by = 0, *tmp;
    int i;
    struct svalue *ret;
    extern struct object *master_ob;

    
    /* Stop if people managed to quit before we got this far */
    if (me->flags & O_DESTRUCTED)
	return 0;
    if (you && (you->flags & O_DESTRUCTED))
	return 0;
    
    /* Find the snooper & snopee */
    for(i = 0 ; i < MAX_PLAYERS && (on == 0 || by == 0); i++) 
    {
	if (all_players[i] == 0)
	    continue;
	if (all_players[i]->ob == me)
	    by = all_players[i];
	else if (all_players[i]->ob == you)
	    on = all_players[i];
    }

    /* Check for permissions with valid_snoop in master */
    if (current_object != master_ob)
    {
	push_object(current_object);
	push_object(me);
	if (you == 0)
	    push_number(0);
	else
	    push_object(you);
	ret = apply_master_ob(M_VALID_SNOOP, 3);
	
	if (!ret || ret->type != T_NUMBER || ret->u.number == 0)
	    return 0;
    }
    /* Stop snoop */
    if (you == 0) 
    {
	if (by == 0)
	    error("Could not find snooper to stop snoop on.\n");
	if (by->snoop_on == 0)
	    return 1;
	by->snoop_on->snoop_by = 0;
	by->snoop_on = 0;
	return 1;
    }

    /* Strange event, but possible, so test for it */
    if (on == 0 || by == 0)
	return 0;

    /* Protect against snooping loops */
    for (tmp = on; tmp; tmp = tmp->snoop_on) 
    {
	if (tmp == by) 
	    return 0;
    }

    /* Terminate previous snoop, if any */
    if (by->snoop_on) 
    {
	by->snoop_on->snoop_by = 0;
	by->snoop_on = 0;
    }
    if (on->snoop_by)
    {
	on->snoop_by->snoop_on = 0;
	on->snoop_by = 0;
    }

    on->snoop_by = by;
    by->snoop_on = on;
    return 1;
    
}

#define	TS_DATA		0
#define	TS_IAC		1
#define	TS_WILL		2
#define	TS_WONT		3
#define	TS_DO		4
#define	TS_DONT		5

void
telnet_neg(char *to, char *from)
{
    int state = TS_DATA;
    int ch;
    char *first = to;

    while(1)
    {
	ch = (*from++ & 0xff);
	switch(state) {
	case TS_DATA:
	    switch(ch)
	    {
	    case IAC:
		state = TS_IAC;
		continue;
	    case '\b':	/* Backspace */
	    case 0x7f:	/* Delete */
		if (to <= first)
		    continue;
		to -= 1;
		continue;
	    default:
		if (ch & 0x80)
		{
		    if (d_flag & DEBUG_TELNET) debug_message("Tel_neg: 0x%x\n", ch);
		    continue;
		}
		*to++ = ch;
		if (ch == 0)
		    return;
		continue;
	    }
	case TS_IAC:
	    switch(ch)
	    {
	    case WILL:
		state = TS_WILL;
		continue;
	    case WONT:
		state = TS_WONT;
		continue;
	    case DO:
		state = TS_DO;
		continue;
	    case DONT:
		state = TS_DONT;
		continue;
	    case DM:
		break;
	    case NOP:
	    case GA:
	    default:
		break;
	    }
	    state = TS_DATA;
	    continue;
	case TS_WILL:
	    if (d_flag & DEBUG_TELNET) debug_message("Will %s\n", telopts[ch]);
	    state = TS_DATA;
	    continue;
	case TS_WONT:
	    if (d_flag & DEBUG_TELNET) debug_message("Wont %s\n", telopts[ch]);
	    state = TS_DATA;
	    continue;
	case TS_DO:
	    if (d_flag & DEBUG_TELNET) debug_message("Do %s\n", telopts[ch]);
	    state = TS_DATA;
	    continue;
	case TS_DONT:
	    if (d_flag & DEBUG_TELNET) debug_message("Dont %s\n", telopts[ch]);
	    state = TS_DATA;
	    continue;
	default:
	    if (d_flag & DEBUG_TELNET) debug_message("Bad state: 0x%x\n", state);
	    state = TS_DATA;
	    continue;
	}
    }
}

#define IPSIZE 200
static struct ipentry
{
    unsigned long addr;
    char *name;
} iptable[IPSIZE];
static int ipcur;

char *
query_ip_name(struct object *ob)
{
    int i;

    if (ob == 0)
	ob = command_giver;
    if (!ob || ob->interactive == 0)
	return 0;
    for(i = 0; i < IPSIZE; i++)
    {
	if (iptable[i].addr ==
	    (ob->interactive->addr.sin_addr.s_addr & 0xffffffff) &&
	    iptable[i].name)
	    return iptable[i].name;
    }
    return inet_ntoa(ob->interactive->addr.sin_addr);
}

static void
add_ip_entry(unsigned long addr, char *name)
{
    int i;

    for(i = 0; i < IPSIZE; i++)
    {
	if (iptable[i].addr == addr)
	    return;
    }
    iptable[ipcur].addr = addr;
    if (iptable[ipcur].name)
	free_string(iptable[ipcur].name);
    iptable[ipcur].name = make_shared_string(name);
    ipcur = (ipcur+1) % IPSIZE;
}

char *
query_ip_number(struct object *ob)
{
    if (ob == 0)
	ob = command_giver;
    if (!ob || ob->interactive == 0)
	return 0;
    return inet_ntoa(ob->interactive->addr.sin_addr);
}

#ifndef INET_NTOA_OK
/*
Note: if the address string is "a.b.c.d" the address number is
      a * 256^3 + b * 256^2 + c * 256 + d

*/

char *
inet_ntoa(struct in_addr ad)
{
    u_long s_ad;
    int a, b, c, d;
    static char addr[20]; /* 16 + 1 should be enough */

    s_ad = ad.s_addr;
    d = s_ad % 256;
    s_ad /= 256;
    c = s_ad % 256;
    s_ad /= 256;
    b = s_ad % 256;
    a = s_ad / 256;
    sprintf(addr, "%d.%d.%d.%d", a, b, c, d);
    return addr;
}
#endif /* INET_NTOA_OK */

char *
query_host_name()
{
    static char name[20];
    
    gethostname(name, sizeof name);
    name[sizeof name - 1] = '\0';	/* Just to make sure */
    return name;
}

struct object *
query_snoop(struct object *ob)
{
    if (ob->interactive->snoop_by == 0)
	return 0;
    return ob->interactive->snoop_by->ob;
}

int
query_idle(struct object *ob)
{
    if (!ob->interactive)
	error("query_idle() of non-interactive object.\n");
    return current_time - ob->interactive->last_time;
}

void
notify_no_command()
{
    char *p,*m;
    extern struct object *vbfc_object;

    if (!command_giver->interactive)
	return;
    p = command_giver->interactive->default_err_message;
    if (p) 
    {
	/* We want 'value by function call' 
	 */
	m = process_string(p, vbfc_object != 0); 
	if (!shadow_catch_message(command_giver, m))
	    add_message(m);
	if (m != p)
	    free(m);
	free_string(p);
	command_giver->interactive->default_err_message = 0;
    }
    else
    {
	add_message("What ?\n");
    }
}

void 
clear_notify()
{
    if (!command_giver || !command_giver->interactive)
	return;
    if (command_giver->interactive->default_err_message)
    {
	free_string(command_giver->interactive->default_err_message);
	command_giver->interactive->default_err_message = 0;
    }
}

void
set_notify_fail_message(char *str)
{
    if (!command_giver || !command_giver->interactive)
	return;
    clear_notify();
    if (command_giver->interactive->default_err_message)
	free_string(command_giver->interactive->default_err_message);
    command_giver->interactive->default_err_message = make_shared_string(str);
}

int 
replace_interactive(struct object *ob, struct object *obfrom,
		    /*IGN*/char *name)
{
    struct svalue *v;

    /*
     * Check with master that exec allowed
     */
    push_string(name, STRING_MALLOC);
    if (ob)
	push_object(ob);
    else
	push_number(0);
    push_object(obfrom);
    v = apply_master_ob(M_VALID_EXEC, 3);
    if (!v || v->type != T_NUMBER || v->u.number == 0)
	return 0;

    /* fprintf(stderr,"DEBUG: %s,%s\n",ob->name,obfrom->name); */
    if (ob && ob->interactive)
	error("Bad argument1 to exec()\n");
    if (!obfrom->interactive)
	error("Bad argument2 to exec()\n");
    if (obfrom->interactive->message_length) {
        write_socket(MESSAGE_FLUSH, obfrom);
    }
    if (ob)
    {
	ob->interactive = obfrom->interactive;
	ob->interactive->ob = ob;
	ob->flags |= O_ONCE_INTERACTIVE;
	add_ref(ob, "exec");
	free_object(obfrom, "exec");
	obfrom->interactive = 0;
	obfrom->flags &= ~O_ONCE_INTERACTIVE;
    }
    else
	remove_interactive(obfrom, 0);
    if (obfrom == command_giver) command_giver = ob;
    return 1;
}

#ifdef DEBUG
/*
 * This is used for debugging reference counts.
 */

void
update_ref_counts_for_players()
{
    int i;

    for (i = 0; i<MAX_PLAYERS; i++)
    {
	if (all_players[i] == 0)
	    continue;
	all_players[i]->ob->extra_ref++;
	if (all_players[i]->input_to)
	    all_players[i]->input_to->ob->extra_ref++;
    }
}
#endif /* DEBUG */

/*
 * Send messages to other muds.
 *
 * The message is sent with udp. If it gets there it gets there...
 */
int
send_udp(char *to_host, int to_port, char *msg)
{
    int ip1, ip2, ip3, ip4;
    struct sockaddr_in name;
    struct hostent *hp, *gethostbyname();

    if (udp_send < 0)
    {
	/* Create socket on which to send. */
	udp_send = socket(AF_INET, SOCK_DGRAM, 0);
    }
	
    if (udp_send < 0)
	return 0;

    if (sscanf(to_host, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4) == 4) 
    {
	name.sin_addr.s_addr = inet_addr(to_host);
	name.sin_family = AF_INET;
    }
    else
    {
	hp = gethostbyname(to_host);
	if (hp == 0) 
	    return 0;

	memcpy(&name.sin_addr, hp->h_addr, hp->h_length);
	name.sin_family = AF_INET;
    }
    name.sin_port = htons(to_port);
    /* Send message. */
    if (sendto(udp_send, msg, strlen(msg), 0, 
	       (struct sockaddr *)&name, sizeof(name)) != strlen(msg))
	return 0;
    return 1;
}