/
lib/objects/
lib/rooms/
lib/scripts/
lib/security/
src/banner/
src/util/
/*
*** DaleMUD	comm.c main communication routines. Based on DIKU and
***		       SillyMUD.
*/


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

#include "protos.h"


void str2ansi(char *p2, char *p1, int start, int stop);
char *ParseAnsiColors(int UsingAnsi, char *txt);
int construct_prompt(char *buf, struct char_data *ch);
void identd_test(struct sockaddr_in in_addr) ;


#define MAX_CONNECTS 256  /* max number of descriptors (connections) */
			  /* THIS IS SYSTEM DEPENDANT, use 64 is not sure! */



#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 */

#define STATE(d) ((d)->connected)

int mud_port;

extern int errno;

/* extern struct char_data *character_list; */
#if HASH
extern struct hash_header room_db;	  /* In db.c */
#else
extern struct room_data *room_db;	  /* In db.c */
#endif

/* extern int top_of_world;             In db.c */




struct descriptor_data *descriptor_list, *next_to_process;
struct txt_block *bufpool = 0;  /* pool of large output buffers */
int     buf_largecount;         /* # of large buffers which exist */
int     buf_overflows;          /* # of overflows of output */
int     buf_switches;           /* # of switches from small to large buf */

/* int slow_nameserver = FALSE; */

int lawful = 0;		/* work like the game regulator */
int slow_death = 0;     /* Shut her down, Martha, she's sucking mud */
int mudshutdown = 0;       /* clean shutdown */
int reboot = 0;         /* reboot the game after a shutdown */
int no_specials = 0;    /* Suppress ass. of special routines */
long Uptime;            /* time that the game has been up */
long SystemFlags;
long TempDis=0;

int pulse;

#if SITELOCK
char hostlist[MAX_BAN_HOSTS][30];  /* list of sites to ban           */
int numberhosts;
#endif

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





#define O_LOG_NAME "output.log"
#define O_MAX_LINES 30000
 
  static FILE *debugf=NULL;
 
void writetofile(char *msg)
{
  static long lines=0;
  char *b[5];
  int i,j;
 
  if(!debugf) {
    if(!(debugf=fopen(O_LOG_NAME,"wr"))) {
      perror(O_LOG_NAME);
      exit(1);
    }
    setvbuf(debugf,NULL,_IOFBF,16386);
  }
 
  if(lines>O_MAX_LINES) {
   rewind(debugf);
   for(i=0;i<5;i++) {
     b[i]=(char *)malloc(MAX_STRING_LENGTH);
     if(fgets(b[i], MAX_STRING_LENGTH, debugf)==NULL) break;
   }
   fclose(debugf);
   if(!(debugf=fopen(O_LOG_NAME,"wr"))) {
     perror(O_LOG_NAME);
     exit(1);
   }
   setvbuf(debugf,NULL,_IOFBF,16386);
   for(j=0;j<i;j++) {
     fputs(b[j],debugf);
if (b[j])
     free(b[j]);
   }
   lines=0;
  }
  fprintf(debugf,"%s", msg);
  lines++;
}
 
void testlog(char *msg)
{
        char buf[MAX_STRING_LENGTH*2];
  sprintf(buf,"->%s\n", msg);
  writetofile(buf);
return;
}
 
 
void check_affected(char *msg,char *msg2)
{
  struct affected_type  *hjp, *old_af;
  struct char_data *c;
  char buf[5000], *p;
  int i,j;

  if(msg)
    sprintf(buf,"%s : ", msg);
  else
    sprintf(buf,"check_affected: ");
  writetofile(buf);
 
  p=strdup("a simple test in check_affected blank text meaning\
  		   nothing to anyone intelligent anyways.... go get'em!\
  		   Okay.... lets see if we can wack an link list somewhere"); 

  for(c=character_list;c;c=c->next)
    if(c && c->affected)
      for(hjp=c->affected;hjp;old_af = hjp, hjp=hjp->next)
        if(hjp->type > MAX_EXIST_SPELL || hjp->type < 0) {
          sprintf(buf,"bogus hjp->type for (%s).\r\n", GET_NAME(c));
          writetofile(buf);
          sprintf(buf,"string in old_af: %s\r\n", old_af);
          writetofile(buf);
          sprintf(buf,"string in cur_af: %s\r\n", hjp);
          writetofile(buf);
          fflush(debugf);
          abort();
        }
 
  writetofile("ok\n");
 free(p);
return;
}

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

int __main ()
{
  return(1);
}

/* jdb code - added to try to handle all the different ways the connections
   can die, and try to keep these 'invalid' sockets from getting to select
*/

int close_socket_fd( int desc)
{
  struct descriptor_data *d;
/*  extern struct descriptor_data *descriptor_list; */


#if LOG_DEBUG
slog("begin close_socket_fd");
#endif

  for (d = descriptor_list;d;d=d->next) {
    if (d->descriptor == desc) {
      close_socket(d);
    }
  }

#if LOG_DEBUG
slog("end close_socket_fd");
#endif
}

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

  extern int WizLock;

#ifdef SITELOCK
 int a;
#endif

#if defined(sun) || defined(NETBSD)
  struct rlimit rl;
  int res;
#endif

#ifdef MALLOC_DEBUG
	malloc_debug(1);	/* some systems might not have this lib */
#endif
  
  mud_port = DFLT_PORT;
  dir = DFLT_DIR;

	SystemFlags =0;

#if LOG_ALL
	SET_BIT(SystemFlags,SYS_LOGALL);
#endif	
	
#if defined(sun) || defined(NETBSD)
/*
**  this block sets the max # of connections.  
*/
#if defined(sun)
   res = getrlimit(RLIMIT_NOFILE, &rl);
   rl.rlim_cur = MAX_CONNECTS;
   res = setrlimit(RLIMIT_NOFILE, &rl);
#endif

#if defined(NETBSD)
   res = getrlimit(RLIMIT_OFILE, &rl);
   rl.rlim_cur = MAX_CONNECTS;
   res = setrlimit(RLIMIT_OFILE, &rl);
#endif   

#endif

#if DEBUG  
/* never seen this function, must be something john was working on, disabled */
/* msw */
/*  malloc_debug(0);  */
#endif

  while ((pos < argc) && (*(argv[pos]) == '-'))	{
    switch (*(argv[pos] + 1))  {
    case 'l':
      lawful = 1;
      log_string("Lawful mode selected.");
      break;
    case 'd':
      if (*(argv[pos] + 2))
	dir = argv[pos] + 2;
      else if (++pos < argc)
	dir = argv[pos];
      else   	{
	log_string("Directory arg expected after option -d.");
	assert(0);
      }
      break;
    case 's':
      no_specials = 1;
      log_string("Suppressing assignment of special routines.");
      break;

	case 'A':
		SET_BIT(SystemFlags,SYS_NOANSI);
		log_string("Disabling ALL color");
		break;
	case 'N':
		SET_BIT(SystemFlags,SYS_SKIPDNS);
		log_string("Disabling DNS");
		break;
	case 'R':
		SET_BIT(SystemFlags,SYS_REQAPPROVE);
		log_string("Newbie authorizes enabled");
		break;
	case 'L':
		SET_BIT(SystemFlags,SYS_LOGALL);
		log_string("Logging all users");
		break;
         
    default:
      sprintf(buf, "Unknown option -% in argument string.",
	      *(argv[pos] + 1));
      log_string(buf);
      break;
    }
    pos++;
  }
  
  if (pos < argc)
    if (!isdigit(*argv[pos]))      	{
      fprintf(stderr, "Usage: %s [-l] [-s] [-d pathname] [ port # ]\n", 
	      argv[0]);
      assert(0);
    }  else if ((mud_port = atoi(argv[pos])) <= 1024)  {
      printf("Illegal port #\n");
      assert(0);
    }
  
  Uptime = time(0);
  
  sprintf(buf, "Running game on port %d.", mud_port);
  log_string(buf);
  
  if (chdir(dir) < 0)	{
    perror("chdir");
    assert(0);
  }
  
  sprintf(buf, "Using %s as data directory.", dir);
  log_string(buf);
  
  srandom(time(0));
  WizLock = FALSE;

#if SITELOCK
  log_string("Blanking denied hosts.");
  for(a = 0 ; a<= MAX_BAN_HOSTS ; a++) 
    strcpy(hostlist[a]," \0\0\0\0");
  numberhosts = 0;

#if LOCKGROVE
  log_string("Locking out Host: oak.grove.iup.edu.");
  strcpy(hostlist[0],"oak.grove.iup.edu");
  numberhosts = 1; 
  log_string("Locking out Host: everest.rutgers.edu.");
  strcpy(hostlist[1],"everest.rutgers.edu");
  numberhosts = 2;
#endif /* LOCKGROVE */

#if PERSONAL_PERM_LOCKOUTS
  numberhosts +=0; 
#endif

#endif



  /* close stdin */
  close(0);

  run_the_game(mud_port);
  return(0);
}



#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);
  
  PROFILE(monstartup((int) 2, etext);)
    
    descriptor_list = NULL;
  
  log_string("Signal trapping.");
  signal_setup();
  
  log_string("Opening mother connection.");
  s = init_socket(port);
  
#ifdef USE_LAWFUL

if (lawful && load() >= 6)
    {
      log_string("System load too high at startup.");
      coma(1);
    }
 
#endif
 
  boot_db();
  
  log_string("Entering game loop.");
  
  game_loop(s);
  
  close_sockets(s); 
  
  PROFILE(monitor(0);)
    
  if (reboot)  {
    log_string("Rebooting.");
    assert(52);           /* what's so great about HHGTTG, anyhow? */
  }
  
  log_string("Normal termination of game.");
}






/* Accept new connects, relay commands, and call 'heartbeat-functs' */
int game_loop(int s)
{
  fd_set input_set, output_set, exc_set;
#if 0
  fd_set tin, tout, tex;
  fd_set mtin, mtout, mtex;
#endif
  static int cap;
  struct timeval last_time, now, timespent, timeout, null_time;
  static struct timeval opt_time;
  char comm[MAX_INPUT_LENGTH];
  char promptbuf[180];
  struct descriptor_data *point, *next_point;
  int mask;
  struct room_data *rm;

/*  extern struct descriptor_data *descriptor_list; */
  extern int pulse;
  extern int maxdesc;
  extern struct time_info_data time_info;  /* In db.c */  

  null_time.tv_sec = 0;
  null_time.tv_usec = 0;
  
  opt_time.tv_usec = OPT_USEC;  /* Init time values */
  opt_time.tv_sec = 0;
#ifdef NETBSD
  gettimeofday(&last_time, NULL); 
#else
  gettimeofday(&last_time, (struct timeval *) 0);
#endif  
  
  maxdesc = s;


  /* !! Change if more needed !! */
  avail_descs = getdtablesize() -2; /* never used, pointless? */
  
//  mask = sigmask(SIGUSR1) | sigmask(SIGUSR2) | sigmask(SIGINT) |
//    sigmask(SIGPIPE) | sigmask(SIGALRM) | sigmask(SIGTERM) |
//      sigmask(SIGURG) | sigmask(SIGXCPU) | sigmask(SIGHUP);
  
  /* Main loop */
  while (!mudshutdown)  {


    /* Check what's happening out there */

    FD_ZERO(&input_set);
    FD_ZERO(&output_set);
    FD_ZERO(&exc_set);

    FD_SET(s, &input_set);
    
#if TITAN
    maxdesc = 0;
    if (cap < 20)
      cap = 20;
    for (point = descriptor_list; point; point = point->next)  {

      if (point->descriptor <= cap && point->descriptor >= cap-20) {
	FD_SET(point->descriptor, &input_set);
	FD_SET(point->descriptor, &exc_set);
	FD_SET(point->descriptor, &output_set);
      
      }

      if (maxdesc < point->descriptor)
	maxdesc = point->descriptor;
    }

    if (cap > maxdesc)
      cap = 0;
    else
      cap += 20;
#else
	dimd_loop();
    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);
      
      if (maxdesc < point->descriptor)
	maxdesc = point->descriptor;
    }
#endif

    /* check out the time */
#ifdef NETBSD
    gettimeofday(&now, NULL); 
#else
    gettimeofday(&now, (struct timeval *) 0);
#endif

    
    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");
      
	/* one of the descriptors is broken... */

      for (point = descriptor_list; point; point = next_point)  {
	next_point = point->next;
	write_to_descriptor(point->descriptor, "\n\r");
      }
      
    }

    if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) {
      perror("Select sleep");
      /*assert(0);*/
    }
    
//    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) {

	      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  {
	    check_affected("Before command issued","");
	     command_interpreter (  point->character, comm);
	    check_affected("After command issued","");
	     }
          } 
	  else if(point->connected == CON_EDITING)
	    RoomEdit(point->character,comm);
          else if(point->connected == CON_OBJ_EDITING)
            ObjEdit(point->character,comm);
          else if(point->connected == CON_MOB_EDITING)
            MobEdit(point->character,comm);
	  else
	    nanny(point, comm); 
	}
    }
    
    
    /* either they are out of the game */
    /* or they want a prompt.          */
memory_check("end for 4, begin for 5");
    for (point = descriptor_list; point; point = next_point) {
	next_point = point->next;

#ifndef BLOCK_WRITE
	if (FD_ISSET(point->descriptor, &output_set) && point->output.head)
#else
	if (FD_ISSET(point->descriptor, &output_set) && *(point->output))
#endif	
	  if (process_output(point) < 0)
	    close_socket(point);
	  else
	    point->prompt_mode = 1;
      }
    
    /* give the people some prompts  */
memory_check("end 5, begin 6");
    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,
				"[Return to continue/Q to quit]");
	  else { 
 
            if(point->character->term == VT100) {
               struct char_data *ch;
               int update = 0;
 
               ch = point->character;
 
               if(GET_MOVE(ch) != ch->last.move) {
                  SET_BIT(update, INFO_MOVE);
                  ch->last.move = GET_MOVE(ch);
		}
               if(GET_MAX_MOVE(ch) != ch->last.mmove) {
                  SET_BIT(update, INFO_MOVE);
                  ch->last.mmove = GET_MAX_MOVE(ch);
		}
               if(GET_HIT(ch) != ch->last.hit) {
                  SET_BIT(update, INFO_HP);
                  ch->last.hit = GET_HIT(ch);
		}
               if(GET_MAX_HIT(ch) != ch->last.mhit) {
                  SET_BIT(update, INFO_HP);
                  ch->last.mhit = GET_MAX_HIT(ch);
		}
               if(GET_MANA(ch) != ch->last.mana) {
                  SET_BIT(update, INFO_MANA);
                  ch->last.mana = GET_MANA(ch);
		}
               if(GET_MAX_MANA(ch) != ch->last.mmana) {
                  SET_BIT(update, INFO_MANA);
                  ch->last.mmana = GET_MAX_MANA(ch);
		}
               if(GET_GOLD(ch) != ch->last.gold) {
                  SET_BIT(update, INFO_GOLD);
                  ch->last.gold = GET_GOLD(ch);
		}
               if(GET_EXP(ch) != ch->last.exp) {
                  SET_BIT(update, INFO_EXP);
                  ch->last.exp = GET_EXP(ch);
		}
               if(update)
                  UpdateScreen(ch, update);
	       sprintf(promptbuf,"> ");
	       } else {
	       construct_prompt(promptbuf,point->character);
	     }
	  write_to_descriptor(point->descriptor,ParseAnsiColors( \
                              IS_SET(point->character->player.user_flags,USE_ANSI), \
                              promptbuf));
	  }
	point->prompt_mode = 0;
	
      }  
    
    
    /* handle heartbeat stuff */
    /* Note: pulse now changes every 1/4 sec  */

    pulse++;
    
    if (!(pulse % PULSE_ZONE))  {
	dlog("Before zone update pulse");
    memory_check("BEfore Zone update");
      zone_update();
      if (lawful)
	gr(s);
    }
    
    
    if (!(pulse % PULSE_RIVER)) {
	dlog("Before river pulse ");
	memory_check("Before riverpulse");    
      RiverPulseStuff(pulse);
    }
    
    if (!(pulse % PULSE_TELEPORT)) {
	dlog("Before Teleport pulse");
	memory_check("Tele pulse");
      TeleportPulseStuff(pulse);
    }
    
    if (!(pulse % PULSE_VIOLENCE)) {
	dlog("Before violence pulse...");
	memory_check("Pulse violence");
	check_mobile_activity(pulse);    
	perform_violence( pulse );
      }

    
    
    if (!(pulse % (SECS_PER_MUD_HOUR*4))){
	dlog("Before hourly tick pulse");
	memory_check(" before pulse weather");
      weather_and_time(1);
      affect_update(pulse);  /* things have been sped up by combining */
      if ( time_info.hours == 1 )
	update_time();
    }

  memory_check("loop end");
    
    if (pulse >= 2400) {
      pulse = 0;
      if (lawful)
	night_watchman();
      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);
	if (!dest) {
	  log_sev("Sending message to null destination.", 5);
	  return(0);
	}
	tmp = queue->head;

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

	if (tmp->text)
		free(tmp->text);
	if (tmp)
		free(tmp);
 return(1);
}


#ifndef BLOCK_WRITE
void write_to_q(char *txt, struct txt_q *queue)
{
  struct txt_block *new;
  char tbuf[256];
  int strl;


  if (!queue) {
    log_string("Output message to non-existant queue");
    return;
  }

  CREATE(new, struct txt_block, 1);
  strl = strlen(txt);


  if (strl < 0 || strl > 35000) {
    log_string("strlen returned bogus length in write_to_q, string was: ");
    for(strl=0;strl<120;strl++) tbuf[strl]=txt[strl]; tbuf[strl]=0;
    log_string(strl);
	if (new)
	    free(new);
    return;
  }

#if 0 			/* Changed for test 2-21 added back to test... */
	memory_check("Before CREATE write_to_q",txt);
  CREATE(new->text, char, strl+1);
	memory_check("After CREATE write_to_q",txt);
  strcpy(new->text, txt);
	memory_check("After strcpy write_to_q",txt);
#else
	 if (!(new->text = strdup(txt))){
		log_string("strdup returned null");
		assert(0);
	}
#endif
	  new->next = NULL;

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

#else
void write_to_q(char *txt, struct txt_q *queue)
{
  struct txt_block *new;
  char tbuf[256];
  int strl;

  if (!queue) {
    log_string("Output message to non-existant queue");
    return;
  }

/*testlog(txt);*/

  CREATE(new, struct txt_block, 1);
  strl = strlen(txt);
  if (strl < 0 || strl > 45000) {
    log_string("strlen returned bogus length in write_to_q, string was:");
    for(strl=0;strl<120;strl++) tbuf[strl]=txt[strl]; tbuf[strl]=0;
    log_string(strl);
if (new)
    free(new);
    return;
  }
  new->text = strdup(txt);
  new->next = NULL;

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

#if BLOCK_WRITE
void write_to_output(char *txt, struct descriptor_data *t)
{
   int size;

   size = strlen(txt);

   /* if we're in the overflow state already, ignore this */
   if (t->bufptr < 0) {
      log_string("over flow stat in write_to_output, comm.c");
      assert(0);
      return;
      }

   /* if we have enough space, just write to buffer and that's it! */
   if (t->bufspace >= size) {
      strcpy(t->output+t->bufptr, txt);
      t->bufspace -= size;
      t->bufptr += size;
   } else {      /* otherwise, try to switch to a large buffer */
      if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE)) {
         /* we're already using large buffer, or even the large buffer
            in't big enough -- switch to overflow state */
         t->bufptr = -1;
         buf_overflows++;
         return;
      }

      buf_switches++;
      /* if the pool has a buffer in it, grab it */
      if (bufpool) {
         t->large_outbuf = bufpool;
         bufpool = bufpool->next;
      } else { /* else create one */
         CREATE(t->large_outbuf, struct txt_block, 1);
         CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE);
         buf_largecount++;
      }

      strcpy(t->large_outbuf->text, t->output);
      t->output = t->large_outbuf->text;
      strcat(t->output, txt);
      t->bufspace = LARGE_BUFSIZE-1 - strlen(t->output);
      t->bufptr = strlen(t->output);
   }
}
#else
	/* we use write_to_q instead of write_to_output */
#endif


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





#ifndef BLOCK_WRITE
/* Empty the queues before closing connection */
void flush_queues(struct descriptor_data *d)
{
	char dummy[MAX_STRING_LENGTH];

	while (get_from_q(&d->output, dummy));
	while (get_from_q(&d->input, dummy));
}
#else
void flush_queues(struct descriptor_data *d)
{
   char buf2[MAX_STRING_LENGTH];

   if (d->large_outbuf) {
      d->large_outbuf->next = bufpool;
      bufpool = d->large_outbuf;
   }

   while (get_from_q(&d->input, buf2))
      ;
}
#endif






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



#if 0
/* trying merc code out */
int init_socket(int port)
{
	int s;
	char *opt;
	char hostname[MAX_HOSTNAME+1]; 
	struct sockaddr_in sa;
	struct hostent *hp;
	struct linger ld;
	
	bzero(&sa, sizeof(struct sockaddr_in));
	gethostname(hostname, MAX_HOSTNAME);

	hp = gethostbyname(hostname);
	if (hp == NULL)	{
		perror("gethostbyname");
 		assert(0); 
	}

	sa.sin_family = hp->h_addrtype;
	sa.sin_port	= htons(port);
	s = socket(AF_INET, SOCK_STREAM, 0);
	if (s < 0) 	{
		perror("Init-socket");
		assert(0);
 	}
	if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
		(char *) &opt, sizeof (opt)) < 0) 	{
		perror ("setsockopt REUSEADDR");
		exit (1);
	}

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


#ifdef NETBSD
if ( bind( s, (struct sockaddr *) &sa, sizeof(sa) ) < 0 )
#else
	if (bind(s, &sa, sizeof(sa), 0) < 0)	
#endif	
        {
	    perror("bind");
	    exit(0);
	}

	listen(s, 5);
	return(s);
}
#else
int init_socket( int port )
{
    static struct sockaddr_in sa_zero;
    struct sockaddr_in sa;
    int x = 1;
    int fd;

    if ( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    {
        perror( "Init_socket: socket" );
        exit( 1 );
    }

    if ( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
    (char *) &x, sizeof(x) ) < 0 )
    {
        perror( "Init_socket: SO_REUSEADDR" );
        close( fd );
        exit( 1 );
    }

    sa              = sa_zero;
    sa.sin_family   = AF_INET;
    sa.sin_port     = htons( port );

    if ( bind( fd, (struct sockaddr *) &sa, sizeof(sa) ) < 0 )
    {
        perror( "Init_socket: bind" );
        close( fd );
        exit( 1 );
    }

    if ( listen( fd, 3 ) < 0 )
    {
        perror( "Init_socket: listen" );
        close( fd );
        exit( 1 );
    }

    return fd;
}
#endif




int new_connection(int s)
{
  struct sockaddr_in isa;
#ifdef sun
   struct sockaddr peer;
#endif
  int i;
  int t;
  char buf[100];
  
  i = sizeof(isa);
#if 0
  getsockname(s, &isa, &i);
#endif
  
  if ((t = accept(s, (struct sockaddr *)&isa, &i)) < 0){
    perror("Accept");
    return(-1);
  }
  nonblock(t);
  
#ifdef sun
    
  i = sizeof(peer);
  if (!getpeername(t, &peer, &i))	{
    *(peer.sa_data + 49) = '\0';
    sprintf(buf, "New connection from addr %s.", peer.sa_data);
    log_string(buf);
  }

#endif
  
  return(t);
}

int new_descriptor(int s)
{

  int desc,size,i;
  struct descriptor_data *newd;
  struct hostent *from;
  struct sockaddr_in sock;
  char buf[200],buf2[200];
 extern char login[];  
  if ((desc = new_connection(s)) < 0)
    return (-1);
  
  
  if ((desc + 1) >= MAX_CONNECTS) {
    struct descriptor_data *d;
    sprintf(buf,"Sorry.. The game is full. (max players %d) Try again later.\n\r",desc);
    write_to_descriptor(desc,buf);
    close(desc);
    
    for (d = descriptor_list; d; d = d->next) {
      if (!d->character)
	close_socket(d);
    }
    return(0);
  }
  else
    if (desc > maxdesc)
      maxdesc = desc;
  
  CREATE(newd, struct descriptor_data, 1);

#if 0
		/* older code replaced */
		
  *newd->host = '\0';
  /* find info */
  size = sizeof(sock);
  if (getpeername(desc, (struct sockaddr *) &sock, &size) < 0)    {
    perror("getpeername");
    *newd->host = '\0';
  }
  
  if(*newd->host == '\0') {
#ifndef sun
    if ((long) strncpy(newd->host, inet_ntoa(sock.sin_addr), 49) > 0)  {
      *(newd->host + 49) = '\0';
      sprintf(buf, "New connection from addr %s: %d: %d", newd->host, desc, maxdesc);
      log_sev(buf,3);
    }
#else
    strcpy(newd->host, (char *)inet_ntoa(&sock.sin_addr));
#endif
  }
	
	/* end old code */
#endif

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

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

   sprintf(buf2, "New connection from addr %s: %d: %d", newd->host, desc, maxdesc);
   log_string(buf2);

#if 0
  identd_test(sock);	/* test stuff */
#endif
  
/* end newer code */

  /* init desc data */
  newd->descriptor = desc;
  newd->connected  = CON_NME;
  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';
#ifndef BLOCK_WRITE
   newd->output.head = NULL;
#else
   newd->output=newd->small_outbuf;
   *(newd->output)='\0';
   newd->bufspace=SMALL_BUFSIZE-1;
   newd->large_outbuf=NULL;
#endif
  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(login, newd);
  SEND_TO_Q("What is thy name? ", newd);

  return(0);
}



#ifndef BLOCK_WRITE
int process_output(struct descriptor_data *t)
{
  char i[MAX_STRING_LENGTH + MAX_STRING_LENGTH];
  
  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) && (t->snoop.snoop_by->desc)) {
      SEND_TO_Q("% ",t->snoop.snoop_by->desc);
      SEND_TO_Q(i,t->snoop.snoop_by->desc);
    }
    if (write_to_descriptor(t->descriptor, i))
      return(-1);
  }
  
  if (!t->connected && !(t->character && !IS_NPC(t->character) && 
	 IS_SET(t->character->specials.act, PLR_COMPACT)))
    if (write_to_descriptor(t->descriptor, "\n\r") < 0)
      return(-1);
  
  return(1);
}
#else

/* SEARCH HERE */
int process_output(struct descriptor_data *t)
{
	static char i[LARGE_BUFSIZE + 20];
  struct descriptor_data *point, *next_point;

   /* start writing at the 2nd space so we can prepend "% " for snoop */
   if (!t->prompt_mode && !t->connected) {
      strcpy(i+2, "\n\r");
      strcat(i+2, t->output);
   } else
      strcpy(i+2, t->output);

   if (t->bufptr < 0) {
       log_string("***** OVER FLOW **** in process_output, comm.c");
       strcat(i+2, "**OVERFLOW**");
      }

  if (!t->connected && !(t->character && !IS_NPC(t->character) && 
	 IS_SET(t->character->specials.act, PLR_COMPACT)))
	strcat(i+2, "\n\r");

   if (write_to_descriptor(t->descriptor, i+2) < 0)
      return -1;

   if (t->snoop.snoop_by) {
      i[0] = '%';
      i[1] = ' ';
      SEND_TO_Q(i, t->snoop.snoop_by->desc);
   }

   /* if we were using a large buffer, put the large buffer on the buffer
      pool and switch back to the small one */
   if (t->large_outbuf) {
      t->large_outbuf->next = bufpool;
      bufpool = t->large_outbuf;
      t->large_outbuf = NULL;
      t->output = t->small_outbuf;
   }

   /* reset total bufspace back to that of a small buffer */
   t->bufspace = SMALL_BUFSIZE-1;
   t->bufptr = 0;
   *(t->output) = '\0';

	return(1);
}

#endif

#if 1	 /* reset to use this code, lets see if it helps */
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)	{
	  if (errno == EWOULDBLOCK)
	     break;
	  perror("Write to socket");
 /* 
  lets see if this stops it from crashing close_socket_fd(desc);
  arioch 
 */
	  return(-1);
	}
      sofar += thisround;
  } 
  while (sofar < total);

  
  return(0);
}
#else
/* merc code */
#define UMIN(a, b)              ((a) < (b) ? (a) : (b))
int write_to_descriptor( int desc, char *txt)
{
    int iStart;
    int nWrite;
    int nBlock, length;
    char buf[256];
    
        length = strlen(txt);

    for ( iStart = 0; iStart < length; iStart += nWrite )
    {
        nBlock = UMIN( length - iStart, 4096 );
        if ( ( nWrite = write( desc, txt + iStart, nBlock ) ) < 0 )
            {
	     if (errno == EWOULDBLOCK)
	       break;
       sprintf(buf,"<#=%d> had a error (%d) in write to descriptor (Broken Pipe?)",
       desc, errno);
       log_string(buf);
             perror( "Write_to_descriptor" );
	     /* close_socket_fd(desc); */
	     return(-1); 
	    }
    }

    return(0); 
}

#endif




/* SEARCH HERE */
#ifndef BLOCK_WRITE
/* base silly process_input */
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, 
			  (8*MAX_INPUT_LENGTH) - (begin + sofar) - 1)) > 0) {
#if 0
			  MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0) {
#endif			  
      sofar += thisround;
    } else {
      if (thisround < 0) {
	if (errno != EWOULDBLOCK) {
	  perror("Read1 - ERROR");
	  return(-1);
	} else {
	  break;
	}
      } else {
	log_string("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) && (t->snoop.snoop_by->desc)){
	  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);
}

#else

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, 
			  (8*MAX_INPUT_LENGTH) - (begin + sofar) - 1)) > 0) {
      sofar += thisround;
    } else {
      if (thisround < 0) {
	if (errno != EWOULDBLOCK) {
	  perror("Read1 - ERROR");
	  return(-1);
	} else {
	  break;
	}
      } else {
	log_string("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) && (t->snoop.snoop_by->desc)){
	  SEND_TO_Q("% ",t->snoop.snoop_by->desc);
	  SEND_TO_Q(tmp,t->snoop.snoop_by->desc);
	  SEND_TO_Q("\n\r",t->snoop.snoop_by->desc);
	}
	
	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);
}
#endif



void close_sockets(int s)
{
  log_string("Closing all sockets.");
  
  while (descriptor_list)
    close_socket(descriptor_list);
  
  close(s);
}





void close_socket(struct descriptor_data *d)
{
  char buf[MAX_STRING_LENGTH];
  struct txt_block *txt, *txt2;


  if (!d) {
	log_string("!d in close_socket");
	  return;
  }

#if LOG_DEBUG  
slog("begin close_socket");
#endif

  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) 	{
       do_save(d->character, "", 0);
      act("$n has lost $s touch with reality.", TRUE, d->character, 0, 0, TO_ROOM);
      sprintf(buf, "Closing link to: %s.", GET_NAME(d->character));
      log_string(buf);
      if (IS_NPC(d->character)) { /* poly, or switched god */
	if (d->character->desc)
	  d->character->orig = d->character->desc->original;
      }
      d->character->desc = 0;

/*      d->character->invis_level = LOW_IMMORTAL;  msw 8/9/94 */
      
     if (!IS_AFFECTED(d->character, AFF_CHARM)) {
	if (d->character->master) {
	  stop_follower(d->character);
	}
      }

    } else {
      if (GET_NAME(d->character)) {
	sprintf(buf, "Losing player: %s.", GET_NAME(d->character));
	log_string(buf);
      }
      free_char(d->character);
    }
  else
    log_string("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 */    
{

  struct descriptor_data *tmp;
    				/* Locate the previous element */
    for (tmp = descriptor_list; tmp && tmp->next != d;tmp = tmp->next);

         if (tmp != NULL) 
             tmp->next = d->next;
          else   {
	log_string(" ERROR< ERROR< ERROR< ERROR< corrupted list...");
  /* not sure where this gets fried, but it keeps poping up now and */
  /* then, let me know if you figure it out. msw 		    */
  
  /* DENIS see if you can figure this out */
  
  /* I know that when a new user cuts link in the login process the first */
  /* time it will get here, how to stop it I do not know		  */
    } /* end bug */

  }/* end inside the list */ 

	if (d->showstr_head)   /* this piece of code causes core dumps on */
	 free(d->showstr_head);   /* ardent titans */
	if (d)
	 free(d);
   
#if LOG_DEBUG
  slog("end close_socket");
#endif
}





void nonblock(int s)
{
  if (fcntl(s, F_SETFL, FNDELAY) == -1)    {
    perror("Noblock");
    assert(0);
  }
}




#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 DikuMUD system operators\n\r\n\r"


/* sleep while the load is too high */
void coma(int s)
{
#ifdef USE_LAWFUL

  fd_set input_set;
  static struct timeval timeout =
    {
      60, 
      0
      };
  int conn;
  
  int workhours(void);
  int load(void);
  
  log_string("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");
      assert(0);
    }
    if (FD_ISSET(s, &input_set))	{
      if (load() < 6){
	log_string("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_string("Working hours collision during coma. Exit.");
      assert(0);
    }
  }
  while (load() >= 6);
  
  log_string("Leaving coma.");
//  sigsetmask(0);
#endif
}



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


char *ParseAnsiColors(int UsingAnsi, char *txt)
{
  static char buf[MAX_STRING_LENGTH] = "";
  char tmp[MAX_INPUT_LENGTH];
  
  register int i,l,f=0;

  buf[0]=0;	
  for(i=0,l=0;*txt;) {
    if(*txt=='$' && (toupper(*(txt+1)) == 'C' || 
                    (*(txt+1)=='$' && toupper(*(txt+2)) == 'C'))) {
      if(*(txt+1)=='$')
        txt+=3;
      else
        txt+=2;
      str2ansi(tmp,txt,0,3);
      
      /* if using ANSI */
      if (UsingAnsi)
           strcat(buf,ansi_parse(tmp));
           else
      /* if not using ANSI   */
          strcat(buf,"");   
      
      txt+=4;
      l=strlen(buf);
      f++;
    }
    else {
      buf[l++]=*txt++;
    }
    buf[l]=0;
  }
  if(f && UsingAnsi)
    strcat(buf,ansi_parse("0007"));
 
return buf;
}


void send_to_char(char *messg, struct char_data *ch)
{
  if (ch)
     if (ch->desc && messg)
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(ch->player.user_flags, USE_ANSI),messg),
	  ch->desc); 
}


void save_all()
{
  struct descriptor_data *i;
  
  for (i = descriptor_list; i; i = i->next)
    if (i->character)
      save_char(i->character,AUTO_RENT);
}

void send_to_all(char *messg)
{
  struct descriptor_data *i;
  
  if (messg)
    for (i = descriptor_list; i; i = i->next)
      if (!i->connected)
/*	SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 

}


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))
/*	  SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 
}

void send_to_desert(char *messg)
{
  struct descriptor_data *i;
  struct room_data *rp;
  extern struct zone_data *zone_table;

  if (messg) {
    for (i = descriptor_list; i; i = i->next) {
      if (!i->connected) {
	if (OUTSIDE(i->character)) {
	  if ((rp = real_roomp(i->character->in_room))!=NULL) {
	    if (IS_SET(zone_table[rp->zone].reset_mode, ZONE_DESERT) ||
		rp->sector_type == SECT_DESERT) {
/*	      SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 

	    }
	  }
	}
      }
    }
  }
}

void send_to_out_other(char *messg)
{
  struct descriptor_data *i;
  struct room_data *rp;
  extern struct zone_data *zone_table;

  if (messg) {
    for (i = descriptor_list; i; i = i->next) {
      if (!i->connected) {
	if (OUTSIDE(i->character)) {
	  if ((rp = real_roomp(i->character->in_room))!=NULL) {
	    if (!IS_SET(zone_table[rp->zone].reset_mode, ZONE_DESERT) &&
	        !IS_SET(zone_table[rp->zone].reset_mode, ZONE_ARCTIC) &&
		rp->sector_type != SECT_DESERT) {
/*	      SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 

	    }
	  }
	}
      }
    }
  }
}


void send_to_arctic(char *messg)
{
  struct descriptor_data *i;
  struct room_data *rp;
  extern struct zone_data *zone_table;

  if (messg) {
    for (i = descriptor_list; i; i = i->next) {
      if (!i->connected) {
	if (OUTSIDE(i->character)) {
	  if ((rp = real_roomp(i->character->in_room))!=NULL) {
	    if (IS_SET(zone_table[rp->zone].reset_mode, ZONE_ARCTIC)) {
/*	      SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),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)
/*	SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 

}


void send_to_zone(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)
	if (real_roomp(i->character->in_room)->zone == 
	    real_roomp(ch->in_room)->zone)
/* 	  SEND_TO_Q(messg, &i->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->character->player.user_flags, USE_ANSI),messg),
	  i); 

}



void send_to_room(char *messg, int room)
{
  struct char_data *i;
  
  if (messg)
    for (i = real_roomp(room)->people; i; i = i->next_in_room)
      if (i->desc)
/* 	SEND_TO_Q(messg, &i->desc->output);*/
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->player.user_flags, USE_ANSI),messg),
	  i->desc); 

}




void send_to_room_except(char *messg, int room, struct char_data *ch)
{
  struct char_data *i;
  
  if (messg)
    for (i = real_roomp(room)->people; i; i = i->next_in_room)
      if (i != ch && i->desc)
/* 	SEND_TO_Q(messg, &i->desc->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->player.user_flags, USE_ANSI),messg),
	  i->desc); 

}

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 = real_roomp(room)->people; i; i = i->next_in_room)
      if (i != ch1 && i != ch2 && i->desc)
/* 	SEND_TO_Q(messg, &i->desc->output); */
	SEND_TO_Q(
	 ParseAnsiColors(IS_SET(i->player.user_flags, USE_ANSI),messg),
	  i->desc); 

}


void str2ansi(char *p2, char *p1, int start, int stop)
{
	int i,j;

	if((start > stop) || (start < 0))
		p2[0] = '\0';    /* null terminate string */
	else
	{
		if (start == stop)        /* will copy only 1 char at pos=start */
		{
			p2[0] = p1[start];
			p2[1] = '\0';
		}
		else {
			j = 0;

            /* start or (start-1) depends on start index */
            /* if starting index for arrays is 0 then use start */
            /* if starting index for arrays is 1 then use start-1 */

            for (i=start;i<=stop;i++)   
				 p2[j++] = p1[i];
			p2[j] = '\0';    /* null terminate the string */
		}
	}

if (strlen(p2)+1 > 5)
	log_string("DOH!");  /* remove this after test period */
}

/* higher-level communication */

/* ACT */
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;
  int KLUDGE = FALSE;
  struct char_data *to;
  char buf[MAX_STRING_LENGTH], tmp[MAX_INPUT_LENGTH];

  extern int WizLock;
  
  if (!str)
    return;
  if (!*str)
    return;
  
  if (ch->in_room <= -1)
    return;  /* can't do it. in room -1 */

/*testlog(str);*/
	
  if (type == TO_VICT)
    to = (struct char_data *) vict_obj;
  else if (type == TO_CHAR)
    to = ch;
  else
    to = real_roomp(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) && AWAKE(to) &&
    !((type == TO_NOTVICT) &&
      (to==(struct char_data *) vict_obj))){
      for (strp = str, point = buf;;)
	if (*strp == '$') {
	  switch (*(++strp)) {

#if 1

/*	  parse ansi colors here 
    $CMBFG, where M is modier, B is back ground color and FG is fore 
    $C0001 would be normal, black back, red fore.
    $C1411 would be bold, blue back, light yellow fore 
*/    
act_switch_c:
	  case 'C': 
	  case 'c': if (IS_SET(to->player.user_flags, USE_ANSI)) {
	  		KLUDGE=TRUE;
		       str2ansi(tmp,strp,1,4);
	  	       i = ansi_parse(tmp); 
		     } else
	  		i = "";
		       ++strp;++strp;++strp;++strp; /* delete nums */
                     break;
#endif	  	   
	  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 '$': 
            if(*strp=='$' && toupper(*(strp+1))=='C' && 
                 isdigit(*(strp+2)) && isdigit(*(strp+3)) ) {
               strp++;
               goto act_switch_c;
            }
            i = "$"; 
	    break;
	  default:
	    log_string("Illegal $-code to act():");
	    log_string(str);
	    break;
	  }
	  
	  while (*point = *(i++))
	    ++point;
	  
	  ++strp;
	  
	}	else if (!(*(point++) = *(strp++)))
	  break;
      
      *(--point) = '\n';
      *(++point) = '\r';
      *(++point) = '\0';
      
      SEND_TO_Q(CAP(buf), to->desc);
      
/* kludge to turn the color back grey, try to move to prompt */
  if (KLUDGE && IS_SET(to->player.user_flags,USE_ANSI))
       SEND_TO_Q(ansi_parse("0007"),to->desc);
    }
    
    if ((type == TO_VICT) || (type == TO_CHAR)) {
      return;
      }
  }
}

int raw_force_all( char *to_force)
{
  struct descriptor_data *i;
  char buf[400];

  for (i = descriptor_list; i; i = i->next)
    if (!i->connected) {
      sprintf(buf, "The game has forced you to '%s'.\n\r", to_force);
      send_to_char(buf, i->character);
      command_interpreter(i->character, to_force);
    }
}

int _affected_by_s(struct char_data *ch, int skill)
{
    struct affected_type *hjp;
    int  fs=0,fa=0;
 
    switch(skill) {
        case SPELL_FIRESHIELD:
            if(IS_AFFECTED(ch,AFF_FIRESHIELD)) fa=1; break;
        case SPELL_SANCTUARY:
            if(IS_AFFECTED(ch,AFF_SANCTUARY))  fa=1; break;
        case SPELL_INVISIBLE:
            if(IS_AFFECTED(ch,AFF_INVISIBLE))  fa=1; break;
        case SPELL_TRUE_SIGHT:
            if(IS_AFFECTED(ch,AFF_TRUE_SIGHT)) fa=1; break;
        case SPELL_PROT_ENERGY_DRAIN:
            if(IS_SET(ch->immune,IMM_DRAIN) || IS_SET(ch->M_immune,IMM_DRAIN))
                fa=1;
            break;
    }
    if(ch->affected)
        for (hjp = ch->affected; hjp; hjp = hjp->next)
            if ( hjp->type == skill )
                fs = (hjp->duration + 1);   /* in case it's 0 */
 
         if(!fa && !fs) return -1;
    else if( fa && !fs) return 999;
    else return fs-1;
}

int construct_prompt(char *outbuf, struct char_data *ch)
{
    struct room_data *rm;
    extern const struct title_type titles[MAX_CLASS][ABS_MAX_LVL];
    char tbuf[255],*pr_scan,*mask;
    long l,exp,texp;
    int i,s_flag=0;
 
    *outbuf=0;
 
    if(ch->specials.prompt==NULL) { /* use default prompts */
        if(IS_IMMORTAL(ch))
            mask="Shadowdale: (type help prompt) H:%h R:%R i%iI+> ";
        else
            mask="Shadowdale: (type help prompt) H:%h M:%m V:%v> ";
    } else {
        mask=ch->specials.prompt;
    }
    for(pr_scan = mask; *pr_scan; pr_scan++) {
        if(*pr_scan == '%') {
            if(*(++pr_scan)=='%') {
                tbuf[0]='%';
                tbuf[1]=0;
            } else {
                switch(*pr_scan) {
                            /* stats for character */
                case 'H': sprintf(tbuf,"%d",GET_MAX_HIT(ch)); break;
                case 'h': sprintf(tbuf,"%d",GET_HIT(ch)); break;
                case 'M': sprintf(tbuf,"%d",GET_MAX_MANA(ch)); break;
                case 'm': sprintf(tbuf,"%d",GET_MANA(ch)); break;
                case 'V': sprintf(tbuf,"%d",GET_MAX_MOVE(ch)); break;
                case 'v': sprintf(tbuf,"%d",GET_MOVE(ch)); break;
                case 'G': sprintf(tbuf,"%ld",GET_BANK(ch)); break;
                case 'g': sprintf(tbuf,"%ld",GET_GOLD(ch)); break;
                case 'X': /* xp stuff */
                    sprintf(tbuf,"%ld",GET_EXP(ch));
                    break;
                case 'x': /* xp left to level (any level, btw..) */
                    for(l=1,i=0,exp=999999999;i<PSI_LEVEL_IND;i++,l<<=1) {
                        if(HasClass(ch,l)) {
                            texp=(titles[i][GET_LEVEL(ch,i)+1].exp)-GET_EXP(ch);
                            if(texp<exp)
                                exp=texp;
                        }
                    }
                    sprintf(tbuf,"%ld",exp);
                    break;
                case 'C':   /* mob condition */
#if 1
		/* we get float errors here! msw */
                    if(ch->specials.fighting) {
	     		if (GET_MAX_HIT(ch->specials.fighting)==0)
     				strcpy(tbuf,"unknown"); else
		{     				
                        i=(100*GET_HIT(ch->specials.fighting))/ \
                        GET_MAX_HIT(ch->specials.fighting);
                             if(i>=100)strcpy(tbuf,"excellent");
                        else if(i>=90) strcpy(tbuf,"few scratches");
                        else if(i>=75) strcpy(tbuf,"small wounds");
                        else if(i>=50) strcpy(tbuf,"wounded");
                        else if(i>=30) strcpy(tbuf,"big nasty");
                        else if(i>=15) strcpy(tbuf,"badly wounded");
                        else if(i>=0)  strcpy(tbuf,"awful");
                        else           strcpy(tbuf,"bleeding");
		}                        
                    } else {
                        strcpy(tbuf,"*");
                    }
#endif

                    break;
                case 'c':   /* tank condition */
#if 0

                    if(ch->specials.fighting && ch->specials.fighting->specials.fighting) {
	if (GET_MAX_HIT(ch->specials.fighting->specials.fighting)==0)
		strcpy(tbuf,"Unknown"); else
		{
                        i=(100*GET_HIT(ch->specials.fighting->specials.fighting))/ \
                        GET_MAX_HIT(ch->specials.fighting->specials.fighting);
                             if(i>=100)strcpy(tbuf,"excellent");
                        else if(i>=90) strcpy(tbuf,"few scratches");
                        else if(i>=75) strcpy(tbuf,"small wounds");
                        else if(i>=50) strcpy(tbuf,"wounded");
                        else if(i>=30) strcpy(tbuf,"big nasty");
                        else if(i>=15) strcpy(tbuf,"badly wounded");
                        else if(i>=0)  strcpy(tbuf,"awful");
                        else           strcpy(tbuf,"bleeding");
		}                        
                    } else {
                        strcpy(tbuf,"*");
                    }
#endif

                    break;
                case 's':
		    s_flag = 1;
                case 'S':   /* affected spells */
                    *tbuf=0;
		    if((i=_affected_by_s(ch,SPELL_FIRESHIELD))!=-1) 
		        strcat(tbuf,(i>1)?"F":"f");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
		    if((i=_affected_by_s(ch,SPELL_SANCTUARY))!=-1) 
		        strcat(tbuf,(i>1)?"S":"s");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
		    if((i=_affected_by_s(ch,SPELL_INVISIBLE))!=-1) 
		        strcat(tbuf,(i>1)?"I":"i");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
		    if((i=_affected_by_s(ch,SPELL_TRUE_SIGHT))!=-1) 
		        strcat(tbuf,(i>1)?"T":"t");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
		    if((i=_affected_by_s(ch,SPELL_PROT_ENERGY_DRAIN))!=-1) 
		        strcat(tbuf,(i>1)?"D":"d");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
		    if((i=_affected_by_s(ch,SPELL_ANTI_MAGIC_SHELL))!=-1) 
		        strcat(tbuf,(i>1)?"A":"a");
      		    else if(s_flag) 
			strcat(tbuf,"-"); 
                    break;
                case 'T':   /* did't implemented.. yet */
                    break;
                case 'R': /* room number for immortals */
                    if(IS_IMMORTAL(ch)) {
                        rm = real_roomp(ch->in_room);
                        if (!rm) {
                            char_to_room(ch, 0);
                            rm = real_roomp(ch->in_room);
                        }
                        sprintf(tbuf,"%ld",rm->number);
                    } else {
                        *tbuf=0;
                    }
                    break;
                case 'i':   /* immortal stuff going */
                    pr_scan++;
                    if(!IS_IMMORTAL(ch)) { 
                      *tbuf=0; 
                      break; 
                    }
                    switch(*pr_scan) {
                    case 'I':   /* invisible status */
                        sprintf(tbuf,"%d",ch->invis_level);
                        break;
                    case 'S':   /* stealth mode */
                        strcpy(tbuf,IS_SET(ch->specials.act, PLR_STEALTH)?"On":"Off");
                        break;
                    case 'N':   /* snoop name */
                        if(ch->desc->snoop.snooping)
                            strcpy(tbuf,ch->desc->snoop.snooping->player.name);
			else 
			    *tbuf=0;
                        break;
                    default:
/*
                        sprintf(tbuf,"Invalid Immmortal Prompt code '%c'",*pr_scan);
                        log_string(tbuf);
*/
                        *tbuf=0;
                        break;
                    }
                    break;
                default:
/*  
                    sprintf(tbuf,"Invalid Prompt code '%c'",*pr_scan);
                    log_string(tbuf);
*/
                    *tbuf=0;
                    break;
                }
            }
        } else { /* end of if ch=='%'  */
            tbuf[0]=*pr_scan;
            tbuf[1]=0;
        }
        strcat(outbuf,tbuf);
    }
}


void UpdateScreen(struct char_data *ch, int update)
{
 char buf[255];
 int size;
 
 size = ch->size;

if (size<=0)
    return;
   
 if(IS_SET(update, INFO_MANA)) {
    sprintf(buf, VT_CURSAVE);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 2, 7);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "          ");
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 2, 7);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "%d(%d)", GET_MANA(ch), GET_MAX_MANA(ch));
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURREST);
    write_to_descriptor(ch->desc->descriptor, buf);
  }
 
 if(IS_SET(update, INFO_MOVE)) {
    sprintf(buf, VT_CURSAVE);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 3, 58);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "          ");
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 3, 58);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "%d(%d)", GET_MOVE(ch), GET_MAX_MOVE(ch));
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURREST);
    write_to_descriptor(ch->desc->descriptor, buf);
  }
 
 if(IS_SET(update, INFO_HP)) {
    sprintf(buf, VT_CURSAVE);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 3, 13);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "          ");
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 3, 13);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "%d(%d)", GET_HIT(ch), GET_MAX_HIT(ch));
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURREST);
    write_to_descriptor(ch->desc->descriptor, buf);
  }
 
 if(IS_SET(update, INFO_GOLD)) {
    sprintf(buf, VT_CURSAVE);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 2, 47);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "                ");
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 2, 47);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "%d", GET_GOLD(ch));
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURREST);
    write_to_descriptor(ch->desc->descriptor, buf);
  }
 
 if(IS_SET(update, INFO_EXP)) {
    sprintf(buf, VT_CURSAVE);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 1, 20);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "                ");
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURSPOS, size - 1, 20);
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, "%d", GET_EXP(ch));
    write_to_descriptor(ch->desc->descriptor, buf);
    sprintf(buf, VT_CURREST);
    write_to_descriptor(ch->desc->descriptor, buf);
  }
}
 
 
void InitScreen(struct char_data *ch)
{
 char buf[255];
 int size;

 size = ch->size; 
 sprintf(buf, VT_HOMECLR);
 send_to_char(buf, ch);
 sprintf(buf, VT_MARGSET, 0, size - 5);
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 4, 1);
 send_to_char(buf, ch);
 sprintf(buf, "-===========================================================================-");
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 3, 1);
 send_to_char(buf, ch);
 sprintf(buf, "Hit Points: ");
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 3, 40);
 send_to_char(buf, ch);
 sprintf(buf, "Movement Points: ");
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 2, 1);
 send_to_char(buf, ch);
 sprintf(buf, "Mana: ");
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 2, 40);
 send_to_char(buf, ch);
 sprintf(buf, "Gold: ");
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 1, 1);
 send_to_char(buf, ch);
 sprintf(buf, "Experience Points: ");
 send_to_char(buf, ch);
 
 ch->last.mana = GET_MANA(ch);
 ch->last.mmana = GET_MAX_MANA(ch);
 ch->last.hit = GET_HIT(ch);
 ch->last.mhit = GET_MAX_HIT(ch);
 ch->last.move = GET_MOVE(ch);
 ch->last.mmove = GET_MAX_MOVE(ch);
 ch->last.exp = GET_EXP(ch);
 ch->last.gold = GET_GOLD(ch);
 
 /* Update all of the info parts */
 sprintf(buf, VT_CURSPOS, size - 3, 13);
 send_to_char(buf, ch);
 sprintf(buf, "%d(%d)", GET_HIT(ch), GET_MAX_HIT(ch));
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 3, 58);
 send_to_char(buf, ch);
 sprintf(buf, "%d(%d)", GET_MOVE(ch), GET_MAX_MOVE(ch));
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 2, 7);
 send_to_char(buf, ch);
 sprintf(buf, "%d(%d)", GET_MANA(ch), GET_MAX_MANA(ch));
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 2, 47);
 send_to_char(buf, ch);
 sprintf(buf, "%d", GET_GOLD(ch));
 send_to_char(buf, ch);
 sprintf(buf, VT_CURSPOS, size - 1, 20);
 send_to_char(buf, ch);
 sprintf(buf, "%d", GET_EXP(ch));
 send_to_char(buf, ch);

 sprintf(buf, VT_CURSPOS, 0, 0);
 send_to_char(buf, ch);

}

void identd_test(struct sockaddr_in in_addr)
{
        struct sockaddr_in addr;
        int fd, addrlen, lport, fport;
        FILE *fp_in, *fp_out;
        char buf[8192], reply_type[81], opsys[81], ident[1024];
        int i;
  
  if( (fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    perror("identd socket");
    return;
  }
                            
  addr.sin_family = AF_INET;
  addr.sin_addr.s_addr = in_addr.sin_addr.s_addr;
  addr.sin_port = htons(113);
  addrlen = sizeof(addr);

  if (connect(fd, (struct sockaddr *)&addr, addrlen) == -1) {
    sprintf(buf,"identd server not responding, errno: %d\n", errno);
    log_string(buf);
    return;
  }
 
  addrlen = sizeof(addr);
  if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) == -1)
    perror("getsockname");
 
  fp_in  = fdopen(fd, "r");
  fp_out = fdopen(fd, "w");
  if (!fp_in || !fp_out)
    perror("fdopen");
 
  fprintf(fp_out, "%d , %d\n", ntohs(in_addr.sin_port), mud_port);
  fflush(fp_out);
 
  if(fgets(buf, sizeof(buf)-1, fp_in) == NULL)
    perror("fgets");
 
  shutdown(fd, 1);
 
  i = sscanf(buf, "%d , %d : %[^ \t\n\r:] : %[^\t\n\r:] : %[^\n\r]",
                &lport, &fport, reply_type, opsys, ident);
  if(i < 3) {
    fprintf(stderr, "fscanf: too few arguments (%d)\n", i);
    return;
  }
  if(strcmp(reply_type, "ERROR") == 0) {
    sprintf(buf, "Ident error: error code: %s\n", opsys);
    log_string(buf);
  } else if(strcmp(reply_type, "USERID") != 0) {
    sprintf(buf, "Ident error: illegal reply type: %s\n", reply_type);
    log_string(buf);
  } else {
    sprintf(buf, "ident data -- system:%s user:%s\n", opsys, ident);
    log_string(buf);
  }
 
  fclose(fp_out);
  fclose(fp_in);
 
  return;
}