/* ************************************************************************
*  file: comm.c , Communication module.                   Part of DIKUMUD *
*  Usage: Communication, central game loop.                               *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
*  All Rights Reserved                                                    *
*  Using *any* part of DikuMud without having read license.doc is         *
*  violating our copyright.
************************************************************************* */

#include <errno.h>


#include <ctype.h>
#include <unistd.h>
/* #include <sys/select.h> -- rios wants this? */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>


#include CONFIG

#if HAVE_STRINGS_H

#include <strings.h>
#endif

#if HAVE_STRING_H
#include <string.h>
#endif

#include <sys/time.h>
#include <sys/timeb.h>
#include <fcntl.h>
#include <signal.h>

#ifndef sigmask
#define dismask(m)	(1 << ((m) - 1))
#endif

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interp.h"
#include "db.h"
#include "time.h"
#include "player.h"
#include "vt100.h"
#include "proto.h"

/* For setrlimit */
#if CONFIG_SYSV
#include <sys/resource.h>
#endif

#define DFLT_PORT 4000        /* default port */
#define MAX_NAME_LENGTH 15
#define MAX_HOSTNAME   256
#define OPT_USEC 250000       /* time delay corresponding to 4 passes/sec */



extern int errno;    /* Why isn't this done in errno.h on alfa??? */

/* externs */

extern int shutdownsave(void);

extern struct room_data *world;	  /* In db.c */
extern int top_of_world;            /* In db.c */
extern struct zone_data *zone_table;
extern int top_of_zone_table;
extern struct time_info_data time_info;  /* In db.c */
extern char help[];
extern struct ban_t *ban_list;		/* In db.c */
extern char *greetings;
extern int log_all;

/* local globals */

struct descriptor_data *descriptor_list, *next_to_process;

int lawful = 0;		/* work like the game regulator */
int slow_death = 0;  /* Shut her down, Martha, she's sucking mud */
int shutdown = 0;    /* clean shutdown */
int reboot = 0;      /* reboot the game after a shutdown */
int no_specials = 0; /* Suppress ass. of special routines */
int req_passwd = 1;  /* Need old password to change to a new one  -Sman */
int override = 0;    /* Allow use of override password -Sman */
int bounce_null_sites = 0; /* Permission for null site names to log on -S */
int make_admin = 0;  /* Do new characters become admins? */
char hostname[MAX_HOSTNAME+1]; /* name of this host */
char log_buf[MAX_STRING_LENGTH]; /* Universal log buffer */

#if CONFIG_IDQD
int id_ask[2],id_answer[2];
void start_idqd();
#endif

struct char_data *silent_for=NULL;/* To hide all output except to this person */
		 /* when used with "silently" -Sman */

FILE *cmdfile=NULL;

int maxdesc, avail_descs;
int tics = 0;        /* for extern checkpointing */

char *get_from_q(struct txt_q *queue);
/* write_to_q is in comm.h for the macro */
int run_the_game(int port);
void game_loop(int s);
int init_socket(int port);
int new_connection(int s);
int new_descriptor(int s);
int process_output(struct descriptor_data *t);
int process_input(struct descriptor_data *t);
void close_sockets(int s);
void close_socket(struct descriptor_data *d);
struct timeval timediff(struct timeval *a, struct timeval *b);
void flush_queues(struct descriptor_data *d);
void nonblock(int s);
void parse_name(struct descriptor_data *desc, char *arg);
int number_playing(void);
void make_prompt(char *prompt,struct descriptor_data *point);

/* extern fcnts */

struct char_data *make_char(char *name, struct descriptor_data *desc);
void boot_db(void);
void zone_update(void);
void affect_update( void ); /* In spells.c */
void point_update( void );  /* In limits.c */
void free_char(struct char_data *ch);
void log(char *str);
void character_activity(void);
void room_procs(void);
void item_procs(void);
void string_add(struct descriptor_data *d, char *str);
void perform_violence(void);
void stop_fighting(struct char_data *ch);
void show_string(struct descriptor_data *d, char *input);
void gr(int s);
void scheduler(void);
int calc_lag(void);

void check_reboot(void);

/* *********************************************************************
*  main game loop and related stuff				       *
********************************************************************* */




int main(int argc, char **argv)
{
    int port;
    char buf[512];
    int pos = 1;
    char *dir;

    port = DFLT_PORT;
    dir = DFLT_DIR;

    while ((pos < argc) && (*(argv[pos]) == '-'))
    {
	switch (*(argv[pos] + 1))
	{
	    case 'a':
		make_admin = 1;
		log("Generating Admin characters.");
		break;
	    case 'l':
		lawful = 1;
		log("Lawful mode selected.");
		break;
	    case 'd':
		if (*(argv[pos] + 2))
		    dir = argv[pos] + 2;
		else if (++pos < argc)
		    dir = argv[pos];
		else
		{
		    log("Directory arg expected after option -d.");
		    exit(0);
		}
		break;
	    case 's':
		no_specials = 1;
		log("Suppressing assignment of special routines.");
		break;
	    case 'p':
		req_passwd = 0;
		log("Allowing changing of password without old one");
		break;
	    case 'n':
		bounce_null_sites = 1;
		log("Bouncing null sites");
		break;
	    default:
		sprintf(buf, "Unknown option -% in argument string.",
		    *(argv[pos] + 1));
		log(buf);
		break;
	}
	pos++;
    }
    
    if (pos < argc)
	if (!isdigit(*argv[pos]))
	{
	    fprintf(stderr, "Usage: %s [-l] [-s] [-p] [-n] [-d pathname] [ port # ]\n", 
		argv[0]);
	    exit(0);
	}
	else if ((port = atoi(argv[pos])) <= 1024)
	{
	    printf("Illegal port #\n");
	    exit(0);
	}

#if CONFIG_IDQD
    start_idqd();
#endif

    sprintf(buf, "Running game on port %d.", port);
    log(buf);

    if (chdir(dir) < 0)
    {
	perror("chdir");
	exit(0);
    }

    sprintf(buf, "Using %s as data directory.", dir);
    log(buf);

    srandom(time(0));
    run_the_game(port);
    return(0);
}

#if CONFIG_IDQD

void start_idqd()
{
    int retval,err;

    /* First setup the pipe for communication */
    pipe(id_ask);
    pipe(id_answer);

    retval=fork();
    if(retval==-1) {
        perror("fork");
        exit(1);
    }

    if(!retval) { /* child process */
        /* may have to change rlimits */

        /* Change signal handling */

        /* Set up descriptors for child */
        close(id_ask[1]); /* idqd doesn't ask itself questions */
        close(id_answer[0]); /* neither does it care about its */
                                     /* own answers */
        close(0); /* don't use regular stdin and stdoutand stderr  */
        close(1);
        close(2);

        dup2(id_ask[0],0); /* put pipes where idqd expects */
        dup2(id_answer[1],1);

        close(id_ask[0]); /* Now dup2'd, we don't need these */
        close(id_answer[1]);

        /* make stderr point to a reasonable place */
        err=open("IDQDLOG",O_WRONLY|O_CREAT,0666);
        if(err==-1) {
            exit(1); /* should leave message somewhere ,though... */
        } else if(err!=2) { /* give it to idqd on stderr */
            dup2(err,2);
            close(err);
        }

        /* exec the idqd daemon */
        if((execlp("idqd",NULL))==-1) {
            fprintf(stderr,"CHILD CANNOT EXEC IDQD!\n");
            exit(1);
        }
    }


    /* Parent process continues */
    close(id_ask[1]);  /* reclaim those descriptors we won't use */
    close(id_answer[0]);
}
#endif

#define PROFILE(x)


/* Init sockets, run game, and cleanup sockets */
int run_the_game(int port)
{
    int s;
    PROFILE(extern etext();)

    void signal_setup(void);
    int load(void);
    void coma(int s);

    PROFILE(monstartup((int) 2, etext);)

    descriptor_list = NULL;

    log("Signal trapping.");
    signal_setup();

    log("Opening mother of all connections.");
    s = init_socket(port);

    if (lawful && load() >= 6)
    {
	log("System load too high at startup.");
	coma(0);
    }

    /* Open the file for logging the last command */
    cmdfile=fopen("lstcmd","w");


    boot_db();

    log("Entering game loop.");

    game_loop(s);

    shutdownsave();

    close_sockets(s); 

    if(cmdfile) {
        fclose(cmdfile);
        cmdfile=NULL;
    }


    PROFILE(monitor(0);)

    if (reboot)
    {
	log("Rebooting.");
	exit(52);            /* what's so great about HHGTTG, anyhow? */
    }

    log("Normal termination of game.");
    return(0);
}






/* Accept new connects, relay commands, and call 'heartbeat-functs' */

void game_loop(int s)
{
    fd_set input_set, output_set, exc_set;
    struct timeval last_time, now, timespent, timeout, null_time;
    static struct timeval opt_time;
    char *comm;
    char prompt[MAX_STRING_LENGTH];
    struct descriptor_data *point, *next_point;
    int pulse = 0, mask, tick_lag=0;
    char buf[100];
#if CONFIG_SYSV
    struct rlimit limit = {100,105};
#endif


    null_time.tv_sec = 0;
    null_time.tv_usec = 0;

    opt_time.tv_usec = OPT_USEC;  /* Init time values */
    opt_time.tv_sec = 0;
    gettimeofday(&last_time, (struct timeval *) 0);

    maxdesc = s;

#if CONFIG_BSD
#if HAVE_SETDTABLESIZE
    retval = setdtablesize(100);
    if (retval == -1)
	log("unable to set table size");
    else {
	sprintf(buf, "%s %d\n", "dtablesize set to: ", retval);
	log(buf);
    }
#endif
    avail_descs = getdtablesize() - 8;  /*  !! Change if more needed !! */
#endif

#if CONFIG_SYSV

        retval = setrlimit(RLIMIT_NOFILE,&limit);
        if (retval == -1)
                log("unable to set resource limit");
        else {
                sprintf(buf, "%s %d\n", "rlimit size set to: ", retval);
                log(buf);
        }

        getrlimit(RLIMIT_NOFILE,&limit);
        avail_descs = limit.rlim_cur - 9;  /*  !! Change if more needed !! */
#endif


    sprintf(buf,"avail_descs set to: %d",avail_descs);
    log(buf);

    mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
	sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) |
	sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP);

    /* Main loop */
    while (!shutdown)
    {
	/* Check what's happening out there */
	FD_ZERO(&input_set);
	FD_ZERO(&output_set);
	FD_ZERO(&exc_set);
	FD_SET(s, &input_set);
	for (point = descriptor_list; point; point = point->next)
	{
	    FD_SET(point->descriptor, &input_set);
	    FD_SET(point->descriptor, &exc_set);
	    FD_SET(point->descriptor, &output_set);
	}

	/* check out the time */
	gettimeofday(&now, (struct timeval *) 0);
	timespent = timediff(&now, &last_time);
	timeout = timediff(&opt_time, &timespent);
	last_time.tv_sec = now.tv_sec + timeout.tv_sec;
	last_time.tv_usec = now.tv_usec + timeout.tv_usec;
	if (last_time.tv_usec >= 1000000)
	{
	    last_time.tv_usec -= 1000000;
	    last_time.tv_sec++;
	}

	sigsetmask(mask);

	if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) 
	    < 0)
	{
	    perror("Select poll");
	    return;
	}

	if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0)
	{
	    perror("Select sleep");
	    exit(1);
	}

	sigsetmask(0);

	/* Respond to whatever might be happening */
	
	/* New connection? */
    	if (FD_ISSET(s, &input_set))
	    if (new_descriptor(s) < 0)
		perror("New connection");

	/* kick out the freaky folks */
	for (point = descriptor_list; point; point = next_point)
	{
	    next_point = point->next;   
	    if (FD_ISSET(point->descriptor, &exc_set))
	    {
		FD_CLR(point->descriptor, &input_set);
		FD_CLR(point->descriptor, &output_set);
		close_socket(point);
	    }
	}

	for (point = descriptor_list; point; point = next_point) 
	{
	    next_point = point->next;
	    if (FD_ISSET(point->descriptor, &input_set)) {
    			if (process_input(point) < 0) 
		    close_socket(point);
		point->newline = FALSE;
	    } else
		point->newline = TRUE;
		}


	/* process_commands; */
	for (point = descriptor_list; point; point = next_to_process)
	{
	    next_to_process = point->next;

	    if ((--(point->wait) <= 0) && (comm=get_from_q(&point->input)))
	    {
		if (point->character && point->connected == CON_PLYNG &&
		    point->character->specials.was_in_room !=	NOWHERE)
		{
		    if (point->character->in_room != NOWHERE)
			char_from_room(point->character);
		    char_to_room(point->character, 
			point->character->specials.was_in_room,0);
		    point->character->specials.was_in_room = NOWHERE;
		    act("$n has returned.",	TRUE, point->character, 0, 0, TO_ROOM);
		}

		point->wait = 1;
		point->idle = 0;

		if (point->str)
		    string_add(point, comm);
		else if (!point->connected) 
		    if (point->showstr_point)
			show_string(point, comm);
		    else
			command_interpreter(point->character, comm);
		else 
		    nanny(point, comm); 
		free(comm);
		/* Cheesy way to force prompts */
		SEND_TO_Q("",point);
	    }
	}


	for (point = descriptor_list; point; point = next_point) 
	{
	    next_point = point->next;
	    if(FD_ISSET(point->descriptor, &output_set) && point->output.head) {

		/* give the people some prompts */
		if (point->str) {
		    write_to_descriptor(point->descriptor, "] ");
		}
		else if (!point->connected) {
		    if (point->showstr_point)  {
			write_to_descriptor(point->descriptor,
			    "*** Press return ***");
		    } 
		    else {
                        make_prompt(prompt,point);
                        SEND_TO_Q(prompt, point);
		    } /* if */
		} /* if */
                if (process_output(point) < 0)
		    close_socket(point);
	    }
        }


	/* handle heartbeat stuff */
	/* Note: pulse now changes every 1/4 sec  */
	pulse++;

        loop_debug();
	char_pulse(pulse);
	
	if (!(pulse % PULSE_ZONE))
	{
	    zone_update();
	    if (lawful)
		gr(s);
	}

	if (!(pulse % PULSE_MOBILE)) {
	    scheduler();
	    character_activity();
	    room_procs();
	    item_procs();
	}

	if (!(pulse % PULSE_VIOLENCE))
	    perform_violence();

	if ((pulse % (SECS_PER_MUD_HOUR*4))==tick_lag){
            hour_debug();
	    weather_and_time(1);
	    affect_update();
	    point_update();
	    if ( time_info.hours == 1 )
		update_time();
            check_idling();
	} else if((pulse % (SECS_PER_MUD_HOUR*4))==(4*SECS_PER_MUD_HOUR-1))
            tick_lag=calc_lag();

        if(!(pulse % (SECS_PER_MUD_DAY*4)))  /* evaluate luck here */
            ;

        if(!(pulse % (SECS_PER_MUD_MONTH*4)))
            gen_month_plot();

        if(!(pulse % (SECS_PER_MUD_YEAR*4)))
            gen_year_plot();

        if((pulse % 4)==1 && number(0,50)==3)
            pulse++;

	if (pulse >= 2400) {
	    pulse = 0;
	    if (lawful)
		night_watchman();
	    check_reboot();
	}

	tics++;        /* tics since last checkpoint signal */
    }
}

int calc_lag()
{
    return(number(0,40));
}

/* ******************************************************************
*  general utility stuff (for local use)                            *
****************************************************************** */

char *get_from_q(struct txt_q *queue)
{
    struct txt_block *tmp;
    char *dest;

	/* Q empty? */
    if (!queue->head)
	return(NULL);

    tmp = queue->head;
    dest = tmp->text;
    queue->head = queue->head->next;

    free(tmp);

    return(dest);
}




void write_to_q(char *txt, struct txt_q *queue)
{
    struct txt_block *new;

    CREATE(new, struct txt_block, 1);
    CREATE(new->text, char, strlen(txt) + 1);

    strcpy(new->text, txt);

    /* Q empty? */
    if (!queue->head)
    {
	new->next = NULL;
	queue->head = queue->tail = new;
    }
    else
    {
	queue->tail->next = new;
	queue->tail = new;
	new->next = NULL;
    }
}
	






struct timeval timediff(struct timeval *a, struct timeval *b)
{
    struct timeval rslt, tmp;

    tmp = *a;

    if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0)
    {
	rslt.tv_usec += 1000000;
	--(tmp.tv_sec);
    }
    if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0)
    {
	rslt.tv_usec = 0;
	rslt.tv_sec =0;
    }
    return(rslt);
}






/* Empty the queues before closing connection */
void flush_queues(struct descriptor_data *d)
{
    char *dummy;

    while((dummy=get_from_q(&d->output)))
	free(dummy);
    while((dummy=get_from_q(&d->input)))
	free(dummy);
}






/* ******************************************************************
*  socket handling							 *
****************************************************************** */




int init_socket(int port)
{
    int s;
    char *opt;
    struct sockaddr_in sa;
    struct hostent *hp;
    struct linger ld;

    bzero(&sa, sizeof(struct sockaddr_in));
    gethostname(hostname, MAX_HOSTNAME);
    sprintf(log_buf,"Running on host '%s'",hostname);
    log(log_buf);
    hp = gethostbyname(hostname);
    if (hp == NULL)
    {
	perror("gethostbyname");
	exit(1);
    }
    sa.sin_family = hp->h_addrtype;
    sa.sin_port	= htons(port);
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) 
    {
	perror("Init-socket");
	exit(1);
	}
    if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
	(char *) &opt, sizeof (opt)) < 0) 
    {
	perror ("setsockopt REUSEADDR");
	exit (1);
    }

    ld.l_onoff = 0;
    ld.l_linger = 0;
    if (setsockopt(s, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) < 0)
    {
	perror("setsockopt LINGER");
	exit(1);
    }
    if (bind(s, &sa, sizeof(sa), 0) < 0)
    {
	perror("bind");
	close(s);
	exit(1);
    }
    listen(s, 3);
    return(s);
}





int new_connection(int s)
{
    struct sockaddr_in isa;
    /* struct sockaddr peer; */
    int i;
    int t;

    i = sizeof(isa);
    getsockname(s, &isa, &i);


    if ((t = accept(s, &isa, &i)) < 0)
    {
	perror("Accept");
	return(-1);
    }
    nonblock(t);

    /*

    i = sizeof(peer);
    if (!getpeername(t, &peer, &i))
    {
	*(peer.sa_data + 49) = '\0';
	sprintf(buf, "New connection from addr %s.\n", peer.sa_data);
	log(buf);
    }

    */

    return(t);
}


int number_playing(void)
{
    struct descriptor_data *d;

    int i;

    for(i=0,d=descriptor_list;d;d=d->next){
	i++;
    }
    return(i);
}


int new_descriptor(int s)
{
    int desc;
    struct descriptor_data *newd;
    int size;
    struct sockaddr_in sock;
    struct hostent *from;
    struct ban_t *tmp;

    if ((desc = new_connection(s)) < 0)
	return (-1);
    

    if ( (number_playing()+6) >= avail_descs)
    {
	write_to_descriptor(desc, "Sorry.. The game is full...\n\r");
	close(desc);
	return(0);
    }
    else
	if (desc > maxdesc)
	    maxdesc = desc;

    CREATE(newd, struct descriptor_data, 1);

    /* find info */
    size = sizeof(sock);
    if (getpeername(desc, (struct sockaddr *) &sock, &size) < 0)
    {
	perror("getpeername");
	*newd->host = '\0';
    }
    else if (!(from = gethostbyaddr((char*)&sock.sin_addr,
	sizeof(sock.sin_addr), AF_INET)))
    {
	perror("gethostbyaddr");
        newd->numeric = sock.sin_addr.s_addr;
        sprintf(newd->host,"%d.%d.%d.%d",
                (newd->numeric & 0x000000FF),
                (newd->numeric & 0x0000FF00) >> 8,
                (newd->numeric & 0x00FF0000) >> 16,
                (newd->numeric & 0xFF000000) >> 24);
    }
    else if(from->h_name[0])
    {
        newd->numeric = sock.sin_addr.s_addr;
	strncpy(newd->host, from->h_name, 49);
	*(newd->host + 49) = '\0';
    }
    else
    {
        newd->numeric = sock.sin_addr.s_addr;
        sprintf(newd->host,"%d.%d.%d.%d",
                (newd->numeric & 0x000000FF),
                (newd->numeric & 0x0000FF00) >> 8,
                (newd->numeric & 0x00FF0000) >> 16,
                (newd->numeric & 0xFF000000) >> 24);
    }

    /* Swiftest: I added the following to ban sites. I don't
    endorse banning of sites, but Copper has few descriptors now
    and some people from certain sites keep abusing access by
    using automated 'autodialers' and leaving connections hanging.
    */
    for(tmp=ban_list;tmp;tmp=tmp->next){
	if(strstr(newd->host,tmp->name)){ /* substring match */
            sprintf(log_buf,"Rejected connect from %s",tmp->name);
            log(log_buf);
	    write_to_descriptor(desc, "Your site has been temporarily banned from Copper!\n\r");
	    close(desc);
	    free(newd);
	    return(0);
	}
    }

    if(bounce_null_sites && !str_cmp("",newd->host)) {
	write_to_descriptor(desc,"Your site name is unparseable!\n\r");
	log("Null site name bounced.");
	close(desc);
	free(newd);
	return(0);
    }

    /* ask about username (later - not ready yet) */
    *newd->user = '\0';
    newd->connected  = CON_NME;
    /* idquery(newd); */

    /* init desc data */
    newd->descriptor = desc;
    newd->wait = 1;
    newd->prompt_mode = 0;
    *newd->buf = '\0';
    newd->str = 0;
    newd->showstr_head = 0;
    newd->showstr_point = 0;
    *newd->last_input= '\0';
    newd->output.head = NULL;
    newd->input.head = NULL;
    newd->next = descriptor_list;
    newd->character = 0;
    newd->original = 0;
    newd->snoop.snooping = 0;
    newd->snoop.snoop_by = 0;

    /* prepend to list */

    descriptor_list = newd;

    SEND_TO_Q(greetings, newd);

    return(0);
}
    


int process_output(struct descriptor_data *t)
{
    char buf[2 * MAX_STRING_LENGTH];
    int ibuf = 0;
    char *pstr;
    int ilen;

    if ( t->newline )
    {
	buf[ibuf++] = '\n';
	buf[ibuf++] = '\r';
    }

    /* Cycle thru output queue */
    while ( ( pstr = get_from_q(&t->output) ) != NULL )
    {  
	if(t->snoop.snoop_by)
	{
	    write_to_q("% ",&t->snoop.snoop_by->output);
	    write_to_q(pstr, &t->snoop.snoop_by->output);
	}
	ilen = strlen( pstr );
	if ( ibuf + ilen > sizeof(buf)-10 )
	    ilen = sizeof(buf)-10 - ibuf;
	memcpy( &buf[ibuf], pstr, ilen );
	ibuf += ilen;
	free( pstr );
	if ( ibuf > sizeof(buf)/2 )
	    break;
    }
    
    buf[ibuf] = '\0';
    return write_to_descriptor( t->descriptor, buf );
}


int write_to_descriptor(int desc, char *txt)
{
    int sofar, thisround, total;

    total = strlen(txt);
    sofar = 0;

    do
    {
	thisround = write(desc, txt + sofar, total - sofar);
	if (thisround < 0)
	{
	    perror("Write to socket");
	    return(-1);
	}
	sofar += thisround;
    } 
    while (sofar < total);

    return(0);
}





int process_input(struct descriptor_data *t)
{
    int sofar, thisround, begin, squelch, i, k, flag;
    char tmp[MAX_INPUT_LENGTH+2], buffer[MAX_INPUT_LENGTH + 60];

    sofar = 0;
    flag = 0;
    begin = strlen(t->buf);

    /* Read in some stuff */
    do
    {
	if ((thisround = read(t->descriptor, t->buf + begin + sofar, 
	    MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0)
	    sofar += thisround;		
	else
	    if (thisround < 0)
		if(errno != EWOULDBLOCK)
					{
		    perror("Read1 - ERROR");
		    return(-1);
		}
		else
		    break;
	    else
	    {
		log("EOF encountered on socket read.");
		return(-1);
	    }
    }
    while (!ISNEWL(*(t->buf + begin + sofar - 1)));	

    *(t->buf + begin + sofar) = 0;

    /* if no newline is contained in input, return without proc'ing */
    for (i = begin; !ISNEWL(*(t->buf + i)); i++)
	if (!*(t->buf + i))
	    return(0);

    /* input contains 1 or more newlines; process the stuff */
    for (i = 0, k = 0; *(t->buf + i);)
    {
	if (!ISNEWL(*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2))))
	    if(*(t->buf + i) == '\b')	 /* backspace */
		if (k)  /* more than one char ? */
		{
		    if (*(tmp + --k) == '$')
			k--;				
		    i++;
	 		}
		else
    				i++;  /* no or just one char.. Skip backsp */
	    else
		if (isascii(*(t->buf + i)) && isprint(*(t->buf + i)))
		{
		    /* trans char, double for '$' (printf)	*/
		    if ((*(tmp + k) = *(t->buf + i)) == '$')
			*(tmp + ++k) = '$';
		    k++;
		    i++;
	 		}
		else
    				i++;
	else
	{
	    *(tmp + k) = 0;
	    if(*tmp == '!')
		strcpy(tmp,t->last_input);
	    else
		strcpy(t->last_input,tmp);

	    write_to_q(tmp, &t->input);

	    if(t->snoop.snoop_by) {
		write_to_q("% ",&t->snoop.snoop_by->output);
		write_to_q(tmp,&t->snoop.snoop_by->output);
		write_to_q("\n\r",&t->snoop.snoop_by->output);
	    }

	    if (flag)
	    {
		sprintf(buffer, 
		    "Line too long. Truncated to:\n\r%s\n\r", tmp);
		if (write_to_descriptor(t->descriptor, buffer) < 0)
		    return(-1);

		/* skip the rest of the line */
		for (; !ISNEWL(*(t->buf + i)); i++);
	    }

	    /* find end of entry */
	    for (; ISNEWL(*(t->buf + i)); i++);

	    /* squelch the entry from the buffer */
	    for (squelch = 0;; squelch++)
		if ((*(t->buf + squelch) = 
		    *(t->buf + i + squelch)) == '\0')
	 			break;
	    k = 0;
	    i = 0;
	}
    }
    return(1);
}




void close_sockets(int s)
{
    log("Closing all sockets.");

    while (descriptor_list)
	close_socket(descriptor_list);

    close(s);
}





void close_socket(struct descriptor_data *d)
{
    struct descriptor_data *tmp;
    char buf[100];

    close(d->descriptor);
    flush_queues(d);
    if (d->descriptor == maxdesc)
	--maxdesc;

    /* Forget snooping */
    if (d->snoop.snooping)
	d->snoop.snooping->snoop.snoop_by = 0;

    if (d->snoop.snoop_by)
	{
	    send_to_char("Your victim is no longer among us.\n\r",d->snoop.snoop_by->character);
	    d->snoop.snoop_by->snoop.snooping = 0;
	}

    if (d->character)
	if(d->connected == CON_PLYNG) {
	    act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
	    if(!IS_SET(d->character->specials.act,PLR_GUEST)) {
		save_char(d->character);
		sprintf(buf, "Closing link to: %s.", GET_NAME(d->character));
		log(buf);
		d->character->desc = 0;
	    } else {
		sprintf(buf,"Guest player %s loses link.", GET_NAME(d->character));
		log(buf);
		extract_char(d->character);
		free_char(d->character);
	    }
	}
	else
	{
	    sprintf(buf, "Losing player: %s.", GET_NAME(d->character));
	    log(buf);

	    free_char(d->character);
	}
    else
	log("Losing descriptor without char.");
	

    if (next_to_process == d)		/* to avoid crashing the process loop */
	next_to_process = next_to_process->next;   

    if (d == descriptor_list) /* this is the head of the list */
	descriptor_list = descriptor_list->next;
    else  /* This is somewhere inside the list */
    {
	/* Locate the previous element */
	for (tmp = descriptor_list; (tmp->next != d) && tmp; 
	    tmp = tmp->next);
	
	tmp->next = d->next;
    }
    if (d->showstr_head)
	free(d->showstr_head);
    free(d);
}





void nonblock(int s)
{
#if CONFIG_SYSV
    if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
#else
    if (fcntl(s, F_SETFL, FNDELAY) == -1)
#endif
    {
	perror("Noblock");
	exit(1);
    }
}




#define COMA_SIGN \
"\n\r\
DikuMUD is currently inactive due to excessive load on the host machine.\n\r\
Please try again later.\n\r\n\
\n\r\
 Sadly,\n\r\
\n\r\
  the Copper DikuMUD system operators\n\r\n\r"


/* sleep while the load is too high */
void coma(int s)
{
    fd_set input_set;
    static struct timeval timeout =
    {
	60, 
	0
    };
    int conn;

    int workhours(void);
    int load(void);

    log("Entering comatose state.");

    sigsetmask(sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
	sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) |
	sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP));


    while (descriptor_list)
	close_socket(descriptor_list);

    FD_ZERO(&input_set);
    do
    {
	FD_SET(s, &input_set);
	if (select(64, &input_set, 0, 0, &timeout) < 0)
	{
	    perror("coma select");
	    exit(1);
	}
	if (FD_ISSET(s, &input_set))
	{
	    if (load() < 6)
	    {
		log("Leaving coma with visitor.");
		sigsetmask(0);
		return;
	    }
	    if ((conn = new_connection(s)) >= 0)
	    {
		write_to_descriptor(conn, COMA_SIGN);
		sleep(2);
		close(conn);
	    }
	}			

	tics = 1;
	if (workhours())
	{
	    log("Working hours collision during coma. Exit.");
	    exit(0);
	}
    }
    while (load() >= 6);

    log("Leaving coma.");
    sigsetmask(0);
}


void make_prompt(char *prompt,struct descriptor_data *point)
{
    bool disp=FALSE;
    char temp[MAX_STRING_LENGTH];

    /* Clean out whatever might be in prompt */
    prompt[0]='\0';

    if(point->character->prefs &&
            IS_SET(point->character->prefs->flags,PLR_STATUS)) {
        sprintf(temp, VT_CURSPOS VT_INVERTT \
" %14s     %50s     " VT_NORMALT VT_CURSPOS,0,0,GET_NAME(point->character),
world[point->character->in_room].name,23,0);
        strcat(prompt, temp);
    }

    if(point->character->prefs &&
            !IS_SET(point->character->prefs->flags,PLR_COMPACT))
        strcat(prompt,"\n\r");

    if (point->character->specials.dispflags & DISP_HP) {
         sprintf(temp, " %dh", GET_HIT(point->character));
         strcat(prompt, temp);
         disp = TRUE;
    } /* if */

    if (point->character->specials.dispflags & DISP_MA) {
         sprintf(temp, " %ds", GET_MANA(point->character));
         strcat(prompt, temp);
         disp = TRUE;
    } /* if */

    if (point->character->specials.dispflags & DISP_MV) {
         sprintf(temp, " %dmv", GET_MOVE(point->character));
         strcat(prompt, temp);
         disp = TRUE;
    } /* if */

    if (disp) {
         strcat(prompt, " > ");
    }
    else {
        strcat(prompt, "> ");
    } /* if */
}


/* ****************************************************************
*	Public routines for system-to-player-communication				*
**************************************************************** */

bool silent(struct char_data *ch)
{
    if(silent_for)
	if(ch != silent_for)
	    return(TRUE);
    return(FALSE);
}

void send_to_char(char *messg, struct char_data *ch)
{
	
    if (ch->desc && messg && !silent(ch))
	write_to_q(messg, &ch->desc->output);
}




void send_to_all(char *messg)
{
    struct descriptor_data *i;

    if (messg)
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected && !silent(i->character))
		write_to_q(messg, &i->output);
}


void send_to_outdoor(char *messg)
{
    struct descriptor_data *i;

    if (messg)
	for (i = descriptor_list; i; i = i->next)
	    if (!i->connected)
		if (OUTSIDE(i->character) && !silent(i->character))
		    write_to_q(messg, &i->output);
}

void send_to_zone_outdoor(int zone,char *messg)
{
    int i;

    if(zone<0 || zone >=top_of_zone_table) {
	log("Bad zone in send_to_zone_outdoor!");
	return;
    }

    if (messg)
/*		for (i = 0; i < top_of_world; i++)
	    if (world[i].zone == zone)*/
	for(i=zone_table[zone].real_bottom;i<=zone_table[zone].real_top;i++) {
	    if(i>=0 && i<top_of_world)
		if (!(world[i].room_flags & INDOORS))
		    send_to_room(messg, i);
	}
}


void send_to_except(char *messg, struct char_data *ch)
{
    struct descriptor_data *i;

    if (messg)
	for (i = descriptor_list; i; i = i->next)
	    if (ch->desc != i && !i->connected && !silent(i->character))
		write_to_q(messg, &i->output);
}



void send_to_room(char *messg, int room)
{
    struct char_data *i;

    if (messg)
	for (i = world[room].people; i; i = i->next_in_room)
	    if (i->desc && !silent(i))
		write_to_q(messg, &i->desc->output);
}




void send_to_room_except(char *messg, int room, struct char_data *ch)
{
    struct char_data *i;

    if (messg)
	for (i = world[room].people; i; i = i->next_in_room)
	    if (i != ch && i->desc && !silent(i))
		write_to_q(messg, &i->desc->output);
}

void send_to_room_except_two
    (char *messg, int room, struct char_data *ch1, struct char_data *ch2)
{
	 struct char_data *i;

	    if (messg)
	     for (i = world[room].people; i; i = i->next_in_room)
		 if (i != ch1 && i!=ch2 && i->desc&&!silent(i))
		     write_to_q(messg, &i->desc->output);
}



/* higher-level communication */


void act(char *str, int hide_invisible, struct char_data *ch,
    struct obj_data *obj, void *vict_obj, int type)
{
    register char *strp, *point, *i;
    struct char_data *to;
    char buf[MAX_STRING_LENGTH];

    if (!str)
	return;
    if (!*str)
	return;

    if (type == TO_VICT)
	to = (struct char_data *) vict_obj;
    else if (type == TO_CHAR)
	to = ch;
    else
	to = world[ch->in_room].people;

    for (; to; to = to->next_in_room)
    {
	if (to->desc && ((to != ch) || (type == TO_CHAR)) &&  
	    (CAN_SEE(to, ch) || !hide_invisible || 
	    (type == TO_VICT)) && AWAKE(to) &&
	    !((type == TO_NOTVICT) && (to == (struct char_data *) vict_obj)))
	{
	    for (strp = str, point = buf;;)
		if (*strp == '$')
		{
		    switch (*(++strp))
		    {
			case 'n': i = PERS(ch, to); break;
			case 'N': i = PERS((struct char_data *) vict_obj, to); break;
			case 'm': i = HMHR(ch); break;
			case 'M': i = HMHR((struct char_data *) vict_obj); break;
			case 's': i = HSHR(ch); break;
			case 'S': i = HSHR((struct char_data *) vict_obj); break;
			case 'e': i = HSSH(ch); break;
			case 'E': i = HSSH((struct char_data *) vict_obj); break;
			case 'o': i = OBJN(obj, to); break;
			case 'O': i = OBJN((struct obj_data *) vict_obj, to); break;
			case 'p': i = OBJS(obj, to); break;
			case 'P': i = OBJS((struct obj_data *) vict_obj, to); break;
			case 'a': i = SANA(obj); break;
			case 'A': i = SANA((struct obj_data *) vict_obj); break;
			case 'T': i = (char *) vict_obj; break;
			case 'F': i = fname((char *) vict_obj); break;
			case '$': i = "$"; break;
			default:
			    log("Illegal $-code to act():");
			    log(str);
			    break;
		    }
		    while((*point = *(i++)))
			++point;
		    ++strp;
		}
		else if (!(*(point++) = *(strp++)))
		    break;

	    *(--point) = '\n';
	    *(++point) = '\r';
	    *(++point) = '\0';

	    if(!silent(to))
		write_to_q(CAP(buf), &to->desc->output);
	}
	if ((type == TO_VICT) || (type == TO_CHAR))
	    return;
    }
}