/* ************************************************************************
*   File: comm.c                                        Part of CircleMUD *
*  Usage: Communication, socket handling, main(), central game loop       *
*                                                                         *
*  All rights reserved.  See license.doc for complete information.        *
*                                                                         *
*  Copyright (C) 1993 by the Trustees of the Johns Hopkins University     *
*  CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991.               *
************************************************************************ */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <limits.h>
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "limits.h"

#define MAX_HOSTNAME	256
#define OPT_USEC	250000  /* time delay corresponding to 4 passes/sec */

/* externs */
extern int	restrict;
extern int	mini_mud;
extern int	no_rent_check;
extern FILE	*player_fl;
extern int	DFLT_PORT;
extern char	*DFLT_DIR;
extern int	MAX_PLAYERS;
extern int	MAX_DESCRIPTORS_AVAILABLE;

extern struct room_data *world;		/* In db.c */
extern int	top_of_world;		/* In db.c */
extern struct time_info_data time_info;	/* In db.c */
extern char	help[];

/* local globals */
struct descriptor_data *descriptor_list, *next_to_process;
int	circle_shutdown = 0;	/* clean shutdown */
int	circle_reboot = 0;	/* reboot the game after a shutdown */
int	no_specials = 0;	/* Suppress ass. of special routines */
int	last_desc = 0;		/* last unique num assigned to a desc. */
int	mother_desc = 0;	/* file desc of the mother connection */
int	maxdesc;		/* highest desc num used */
int	avail_descs;		/* max descriptors available */
int	tics = 0;		/* for extern checkpointing */
extern byte	nameserver_is_slow;	/* see config.c */
extern byte	auto_save;		/* see config.c */
extern int	autosave_time;		/* see config.c */

/* functions in this file */
int	get_from_q(struct txt_q *queue, char *dest);
void	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);
void	get_inet_dotaddress(struct sockaddr_in addrstruct, char *dest);
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);

/* extern fcnts */
void	boot_db(void);
void	zone_update(void);
void	affect_update(void); /* In spells.c */
void	point_update(void);  /* In limits.c */
void	mobile_activity(void);
void	string_add(struct descriptor_data *d, char *str);
void	perform_violence(void);
void	show_string(struct descriptor_data *d, char *input);
void	check_reboot(void);
int	isbanned(char *hostname);
void	weather_and_time(int mode);



/* *********************************************************************
*  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 '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 'm':
	 mini_mud = 1;
	 no_rent_check = 1;
	 log("Running in minimized mode & with no rent check.");
	 break;
      case 'q':
	 no_rent_check = 1;
	 log("Quick boot mode -- rent check supressed.");
	 break;
      case 'r':
	 restrict = 1;
	 log("Restricting game -- no new players allowed.");
	 break;
      case 's':
	 no_specials = 1;
	 log("Suppressing assignment of special routines.");
	 break;
      default:
	 sprintf(buf, "SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1));
	 log(buf);
	 break;
      }
      pos++;
   }

   if (pos < argc)
      if (!isdigit(*argv[pos])) {
	 fprintf(stderr, "Usage: %s [-m] [-q] [-r] [-s] [-d pathname] [ port # ]\n", argv[0]);
	 exit(0);
      }
      else if ((port = atoi(argv[pos])) <= 1024) {
	 printf("Illegal port #\n");
	 exit(0);
      }

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

   if (chdir(dir) < 0) {
      perror("Fatal error changing to data directory");
      exit(0);
   }

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

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





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

   void	signal_setup(void);

   descriptor_list = NULL;

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

   log("Opening mother connection.");
   mother_desc = s = init_socket(port);

   boot_db();

   log("Entering game loop.");

   game_loop(s);

   close_sockets(s);
   fclose(player_fl);

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

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






/* 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[MAX_INPUT_LENGTH];
   char	prompt[MAX_INPUT_LENGTH];
   struct descriptor_data *point, *next_point;
   int	pulse = 0, mins_since_crashsave = 0, mask;
   int	sockets_connected, sockets_playing;
   bool disp;
   char	buf[100];

   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 timezone *) 0);

   maxdesc = s;


#if defined (OPEN_MAX)
   avail_descs = OPEN_MAX - 8;
#elif defined (USE_TABLE_SIZE)
   {
      int retval;

      retval = setdtablesize(64);
      if (retval == -1)
        log("SYSERR: unable to set table size");
      else {
         sprintf(buf, "%s %d\n", "dtablesize set to: ", retval);
         log(buf);
      }
      avail_descs = getdtablesize() - 8;
   }
#else
   avail_descs = MAX_DESCRIPTORS_AVAILABLE;
#endif

   avail_descs = MIN(avail_descs, MAX_PLAYERS);

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

   /* Main loop */
   while (!circle_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 timezone *) 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);
      }

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

	 if ((--(point->wait) <= 0) && get_from_q(&point->input, comm)) {
	    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);
	       point->character->specials.was_in_room = NOWHERE;
	       act("$n has returned.", 	TRUE, point->character, 0, 0, TO_ROOM);
	    }

	    point->wait = 1;
	    if (point->character)
	       point->character->specials.timer = 0;
	    point->prompt_mode = 1;

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


      for (point = descriptor_list; point; point = next_point) {
	 next_point = point->next;
	 if (FD_ISSET(point->descriptor, &output_set) && point->output.head)
	    if (process_output(point) < 0)
	       close_socket(point);
	    else
	       point->prompt_mode = 1;
      }

      /* kick out the Phreaky Pholks II  -JE */
      for (point = descriptor_list; point; point = next_to_process) {
	 next_to_process = point->next;
	 if (STATE(point) == CON_CLOSE) 
	    close_socket(point);
      }

      /* give the people some prompts */
      for (point = descriptor_list; point; point = point->next)
	 if (point->prompt_mode) {
	    if (point->str) {
	       write_to_descriptor(point->descriptor, "] ");
	    } else if (!point->connected) {
	       if (point->showstr_point) {
		  write_to_descriptor(point->descriptor, "*** Press return to continue, q to quit ***");
	       } else {
		  if (GET_INVIS_LEV(point->character))
		     sprintf(prompt, "i%d> ", GET_INVIS_LEV(point->character));
		  else {
		     strcpy(prompt, "|");
		     disp = FALSE;

		     if ((PRF_FLAGGED(point->character, PRF_DISPHP)) || 
		         ((PRF_FLAGGED(point->character, PRF_DISPAUTO) && 
		         ((GET_HIT(point->character) / (float)GET_MAX_HIT(point->character)) <
		         0.30)))) {
			sprintf(prompt, "%s %dH", prompt, GET_HIT(point->character));
			disp = TRUE;
		     }

		     if ((PRF_FLAGGED(point->character, PRF_DISPMANA)) || 
		         ((PRF_FLAGGED(point->character, PRF_DISPAUTO) && 
		         ((GET_MANA(point->character) / (float)GET_MAX_MANA(point->character)) <
		         0.30)))) {
			sprintf(prompt, "%s %dM", prompt, GET_MANA(point->character));
			disp = TRUE;
		     }

		     if ((PRF_FLAGGED(point->character, PRF_DISPMOVE)) || 
		         ((PRF_FLAGGED(point->character, PRF_DISPAUTO) && 
		         ((GET_MOVE(point->character) / (float)GET_MAX_MOVE(point->character)) <
		         0.30)))) {
			sprintf(prompt, "%s %dV", prompt, GET_MOVE(point->character));
			disp = TRUE;
		     }

		     if (disp)
			strcat(prompt, " > ");
		     else
			strcpy(prompt, "> ");
		  }

		  write_to_descriptor(point->descriptor, prompt);
	       }
	    }
	    point->prompt_mode = 0;
	 }


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

      pulse++;

      if (!(pulse % PULSE_ZONE)) {
	 zone_update();
      }

      if (!(pulse % PULSE_MOBILE))
	 mobile_activity();

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

      if (!(pulse % (SECS_PER_MUD_HOUR * 4))) {
	 weather_and_time(1);
	 affect_update();
	 point_update();
	 fflush(player_fl);
      }

      if (auto_save)
         if (!(pulse % (60 * 4))) /* one minute */
	    if (++mins_since_crashsave >= autosave_time) {
	       mins_since_crashsave = 0;
	       Crash_save_all();
	    }

      if (!(pulse % 1200)) {
	 sockets_connected = sockets_playing = 0;

	 for (point = descriptor_list; point; point = next_point) {
	    next_point = point->next;
	    sockets_connected++;
	    if (!point->connected)
	       sockets_playing++;
	 }

	 sprintf(buf, "nusage: %-3d sockets connected, %-3d sockets playing",
	     sockets_connected,
	     sockets_playing);
	 log(buf);

#ifdef RUSAGE
	  {
	    struct rusage rusagedata;

	    getrusage(0, &rusagedata);
	    sprintf(buf, "rusage: %d %d %d %d %d %d %d",
	        rusagedata.ru_utime.tv_sec,
	        rusagedata.ru_stime.tv_sec,
	        rusagedata.ru_maxrss,
	        rusagedata.ru_ixrss,
	        rusagedata.ru_ismrss,
	        rusagedata.ru_idrss,
	        rusagedata.ru_isrss);
	    log(buf);
	 }
#endif

      }

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

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


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

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

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

   tmp = queue->head;
   strcpy(dest, queue->head->text);
   queue->head = queue->head->next;

   free(tmp->text);
   free(tmp);

   return(1);
}




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)
{
   while (get_from_q(&d->output, buf2)) 
      ;
   while (get_from_q(&d->input, buf2)) 
      ;
}





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



int	init_socket(int port)
{
   int	s;
   char	*opt;
   char	hostname[MAX_HOSTNAME+1];
   struct sockaddr_in sa;
   struct hostent *hp;

   bzero(&sa, sizeof(struct sockaddr_in ));
   gethostname(hostname, MAX_HOSTNAME);

   hp = gethostbyname(hostname);
   if (hp == NULL) {
      perror("gethostbyname");
      exit(1);
   }
   sa.sin_family = hp->h_addrtype;
   sa.sin_port	 = htons(port);

   if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      perror("Init-socket");
      exit(1);
   }

   if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof (opt)) < 0) {
      perror ("setsockopt REUSEADDR");
      exit (1);
   }

#ifdef USE_LINGER
   {
      struct linger ld;

      ld.l_onoff = 0;
      ld.l_linger = 1000;
      if (setsockopt(s, SOL_SOCKET, SO_LINGER, &ld, sizeof(ld)) < 0) {
         perror("setsockopt LINGER");
         exit(1);
      }
   }
#endif

   if (bind(s, (struct sockaddr *) & sa, sizeof(sa)) < 0) {
      perror("bind");
      close(s);
      exit(1);
   }
   listen(s, 3);
   return(s);
}





int	new_connection(int s)
{
   struct sockaddr_in isa;
   int	i, t;

   i = sizeof(isa);
   getsockname(s, (struct sockaddr *) & isa, &i);

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




int	new_descriptor(int s)
{
   int	desc;
   struct descriptor_data *newd, *point, *next_point;
   int	size, sockets_connected, sockets_playing, i;
   struct sockaddr_in sock;
   struct hostent *from;
   extern char *GREETINGS;

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

   sockets_connected = sockets_playing = 0;

   for (point = descriptor_list; point; point = next_point) {
      next_point = point->next;
      sockets_connected++;
      if (!point->connected)
	 sockets_playing++;
   }

   /*	if ((maxdesc + 1) >= avail_descs) */
   if (sockets_connected >= avail_descs) {
      write_to_descriptor(desc, "Sorry, CircleMUD is full right now... try again later!  :-)\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 (nameserver_is_slow ||
      !(from = gethostbyaddr((char *)&sock.sin_addr,
      sizeof(sock.sin_addr), AF_INET))) {
         if (!nameserver_is_slow)
	    perror("gethostbyaddr");
         i = sock.sin_addr.s_addr;
          sprintf(newd->host, "%d.%d.%d.%d", (i & 0x000000FF),
          (i & 0x0000FF00) >> 8, (i & 0x00FF0000) >> 16,
          (i & 0xFF000000) >> 24 );
   } else {
      strncpy(newd->host, from->h_name, 49);
      *(newd->host + 49) = '\0';
   }

   if (isbanned(newd->host) == BAN_ALL) {
      close(desc);
      sprintf(buf2, "Connection attempt denied from [%s]", newd->host);
      mudlog(buf2, CMP, LEVEL_GOD, TRUE);
      free(newd);
      return(0);
   }

/*  Uncomment this if you want new connections logged.  It's usually not
    necessary, and just adds a lot of unnecessary bulk to the logs.

   sprintf(buf2, "New connection from [%s]", newd->host);
   log(buf2);
*/
   /* init desc data */
   newd->descriptor = desc;
   newd->connected = CON_NME;
   newd->bad_pws = 0;
   newd->pos = -1;
   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;
   newd->login_time = time(0);

   if (++last_desc == 1000)
      last_desc = 1;
   newd->desc_num = last_desc;

   /* prepend to list */

   descriptor_list = newd;

   SEND_TO_Q(GREETINGS, newd);
   SEND_TO_Q("By what name do you wish to be known? ", newd);

   return(0);
}




int	process_output(struct descriptor_data *t)
{
   static char	i[MAX_STRING_LENGTH + 1];

   if (!t->prompt_mode && !t->connected)
      if (write_to_descriptor(t->descriptor, "\n\r") < 0)
	 return(-1);

   /* Cycle thru output queue */
   while (get_from_q(&t->output, i)) {
      if (t->snoop.snoop_by) {
	 write_to_q("% ", &t->snoop.snoop_by->desc->output);
	 write_to_q(i, &t->snoop.snoop_by->desc->output);
      }
      if (write_to_descriptor(t->descriptor, i))
	 return(-1);
   }

   if (!t->connected && !(t->character && !IS_NPC(t->character) && 
       PRF_FLAGGED(t->character, PRF_COMPACT)))
      if (write_to_descriptor(t->descriptor, "\n\r") < 0)
	 return(-1);

   return(1);
}


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->desc->output);
	    write_to_q(tmp, &t->snoop.snoop_by->desc->output);
	    write_to_q("\n\r", &t->snoop.snoop_by->desc->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->desc->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);
      d->snoop.snoop_by->desc->snoop.snooping = 0;
   }

   if (d->character)
      if (d->connected == CON_PLYNG) {
	 save_char(d->character, NOWHERE);
	 act("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
	 sprintf(buf, "Closing link to: %s.", GET_NAME(d->character));
	 mudlog(buf, NRM, MAX(LEVEL_IMMORT, GET_INVIS_LEV(d->character)), TRUE);
	 d->character->desc = 0;
      }
      else {
	 sprintf(buf, "Losing player: %s.", GET_NAME(d->character));
	 mudlog(buf, CMP, MAX(LEVEL_IMMORT, GET_INVIS_LEV(d->character)), TRUE);
	 free_char(d->character);
      }
   else
      mudlog("Losing descriptor without char.", CMP, LEVEL_IMMORT, TRUE);

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


#if defined(SVR4) || defined(LINUX)

void	nonblock(int s)
{
   int	flags;
   flags = fcntl(s, F_GETFL);
   flags |= O_NONBLOCK;
   if (fcntl(s, F_SETFL, flags) < 0) {
      perror("Fatal error executing nonblock (comm.c)");
      exit(1);
   }
}

#else

void	nonblock(int s)
{
   if (fcntl(s, F_SETFL, FNDELAY) == -1) {
      perror("Fatal error executing nonblock (comm.c)");
      exit(1);
   }
}

#endif



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



void	send_to_char(char *messg, struct char_data *ch)
{
   if (ch->desc && messg)
      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)
	    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))
	       write_to_q(messg, &i->output);
}


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)
	    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)
	    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)
	    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)
	    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;
   static 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) && 
          !PLR_FLAGGED(to, PLR_WRITING) && 
          !((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("SYSERR: Illegal $-code to act():");
		  strcpy(buf1, "SYSERR: ");
		  strcat(buf1, str);
		  log(buf1);
		  break;
	       }
	       while ((*point = *(i++)))
		  ++point;
	       ++strp;
	    }
	    else if (!(*(point++) = *(strp++)))
	       break;

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

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

