rm6/
rm6/clans/
rm6/councils/
rm6/deity/
rm6/doc/mudprogs/
rm6/exchange/
rm6/gods/
rm6/homes/
rm6/nations/
rm6/player/
rm6/player/a/
rm6/src/RCS/
rm6/src/dmalloc/
rm6/src/dmalloc/bin/
rm6/src/dmalloc/include/
rm6/src/dmalloc/lib/
rm6/src/scripts/
rm6/src/utils/
/****************************************************************************
 * ResortMUD Version 5.0 was mainly programmed by Ntanel, Garinan, Josh,    *
 * Badastaz, Digifuzz, Senir, Kratas, Scion, Shogar and Tagith.             *
 * ------------------------------------------------------------------------ *
 * Copyright (C) 1996 - 2001 Haslage Net Electronics: MudWorld of Lorain,   *
 * Ohio.    ALL RIGHTS RESERVED    See /doc/RMLicense.txt for more details. *
 ****************************************************************************/

/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			 Low-level communication module			    *
 ****************************************************************************/

#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "mud.h"
#include "gboards.h"
#include "olc.h"

/*
 * Socket and TCP/IP stuff.
 */
#ifdef WIN32
#include <io.h>
#include <winsock2.h>
#undef EINTR
#undef EMFILE
#define EINTR WSAEINTR
#define EMFILE WSAEMFILE
#define EWOULDBLOCK WSAEWOULDBLOCK
#define MAXHOSTNAMELEN 32

#define  TELOPT_ECHO        '\x01'
#define  GA                 '\xF9'
#define  SB                 '\xFA'
#define  WILL               '\xFB'
#define  WONT               '\xFC'
#define  DO                 '\xFD'
#define  DONT               '\xFE'
#define  IAC                '\xFF'
void bailout( void );
void shutdown_checkpoint( void );
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#endif

const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' };
const char go_ahead_str[] = { IAC, GA, '\0' };

#ifdef MCCP
const char eor_on_str[] = { IAC, WILL, TELOPT_EOR, '\0' };
const char compress_on_str[] = { IAC, WILL, TELOPT_COMPRESS, '\0' };
#endif

/* For ispell -- Kratas */
#ifdef ISPELL
void ispell_init args( ( void ) );
void ispell_done args( ( void ) );
#endif

void save_sysdata args( ( SYSTEM_DATA sys ) );
void save_timedata args( ( void ) );
void drop_artifacts( CHAR_DATA * ch, OBJ_DATA * obj );
void save_world( CHAR_DATA * ch );

#ifdef WEBSVR
/* Web Server Handler Functions */
void handle_web args( ( void ) );
bool init_web args( ( int wport ) );
void shutdown_web args( ( void ) );
#endif

/*
 * Global variables.
 */
IMMORTAL_HOST *immortal_host_start; /* Start of Immortal legal domains */
IMMORTAL_HOST *immortal_host_end;   /* End of Immortal legal domains */
DESCRIPTOR_DATA *first_descriptor;  /* First descriptor     */
DESCRIPTOR_DATA *last_descriptor;   /* Last descriptor      */
DESCRIPTOR_DATA *d_next;   /* Next descriptor in loop */
int num_descriptors;
bool mud_down; /* Shutdown       */
bool service_shut_down; /* Shutdown by operator closing down service */
time_t boot_time;
HOUR_MIN_SEC set_boot_time_struct;
HOUR_MIN_SEC *set_boot_time;
struct tm *new_boot_time;
struct tm new_boot_struct;
char str_boot_time[MAX_INPUT_LENGTH];
char lastplayercmd[MAX_INPUT_LENGTH * 2];
time_t current_time; /* Time of this pulse      */
int port;   /* Port number to be used       */
int control;   /* Controlling descriptor  */
int newdesc;   /* New descriptor    */
fd_set in_set; /* Set of desc's for reading  */
fd_set out_set;   /* Set of desc's for writing  */
fd_set exc_set;   /* Set of desc's with errors  */
int maxdesc;
char *alarm_section = "(unknown)";

/*
 * OS-dependent local functions.
 */
void game_loop args( (  ) );
int init_socket args( ( int mport ) );
void new_descriptor args( ( int new_desc ) );
bool read_from_descriptor args( ( DESCRIPTOR_DATA * d ) );
bool write_to_descriptor args( ( DESCRIPTOR_DATA * d, char *txt, int length ) );
bool write_to_descriptor2 args( ( int desc, char *txt, int length ) );

/*
 * Other local functions (OS-independent).
 */
bool check_reconnect args( ( DESCRIPTOR_DATA * d, char *name, bool fConn ) );
void nanny( DESCRIPTOR_DATA * d, char *argument );
bool check_playing args( ( DESCRIPTOR_DATA * d, char *name, bool kick ) );
bool check_login args( ( DESCRIPTOR_DATA * d, char *name ) );
int main args( ( int argc, char **argv ) );
bool flush_buffer args( ( DESCRIPTOR_DATA * d, bool fPrompt ) );
void read_from_buffer args( ( DESCRIPTOR_DATA * d ) );
void stop_idling args( ( CHAR_DATA * ch ) );
void free_desc args( ( DESCRIPTOR_DATA * d ) );
void display_prompt args( ( DESCRIPTOR_DATA * d ) );
int make_color_sequence args( ( const char *col, char *buf, DESCRIPTOR_DATA * d ) );
void set_pager_input args( ( DESCRIPTOR_DATA * d, char *argument ) );
bool pager_output args( ( DESCRIPTOR_DATA * d ) );
void mail_count args( ( CHAR_DATA * ch ) );
void tax_player args( ( CHAR_DATA * ch ) );

bool is_multiplaying( DESCRIPTOR_DATA * desc )
{
   DESCRIPTOR_DATA *d;
   int count = 0, pkcount = 0, pcount = 0, icount = 0;
   char buf[MSL];

   for( d = first_descriptor; d; d = d->next )
   {
      if( d->character != NULL && d->character->desc->host == NULL )
         continue;
      if( !str_cmp( d->host, desc->host ) )
      {
         /*
          * pkcount is for pkillers from the same ip address 
          */
         if( d->character != NULL && IS_PKILL( d->character ) )
            pkcount++;
         /*
          * pcount is for peaceful players from the same ip address 
          */
         if( d->character != NULL && !IS_PKILL( d->character ) )
            pcount++;
         /*
          * icount is for immortals from the same ip address 
          */
         if( d->character != NULL && IS_IMMORTAL( d->character ) )
            icount++;
         /*
          * count is for a total not counting immortals 
          */
         count++;
      }
   }
   /*
    * This is to log how many connections it found and all uncomment to get an idea of how it works 
    */
//  sprintf(buf, "%s has %d connections: %d pkillers: %d peacefuls: %d immortals.",
//      desc->host, count, pkcount, pcount, icount);
//  log_string(buf);
   /*
    * This is to limit your immortals from playing players and immortals at the same time 
    */
   /*
    * This is incase you want to limit peacefuls and pkillers from being logged on from the same ip 
    */
   if( ( pcount >= sysdata.pklimit && pkcount >= sysdata.mplimit )
       || ( pcount >= sysdata.mplimit && pkcount >= sysdata.pklimit ) )
   {
      sprintf( buf, "%s has %d peacefuls connected and %d pkillers.", desc->host, pcount, pkcount );
      log_string( buf );
      return TRUE;
   }
   /*
    * This is incase you want to put a limit on how many pkillers they can have on at a time 
    */
   if( pkcount > sysdata.pklimit )
   {
      sprintf( buf, "%s has %d pkill connections.", desc->host, pkcount );
      log_string( buf );
      return TRUE;
   }
   return FALSE;
}

extern int num_changes( void );
extern MORPH_DATA *morph_start;

void free_hpk_list( void );
void unlink_social( SOCIALTYPE * social );
bool destroy_hitable( char *keyword );
void close_all_areas( void );

typedef struct hiscore_entry HISCORE_ENTRY;
typedef struct hiscore HISCORE;
struct hiscore_entry
{
   char *name;
   int score;

   HISCORE_ENTRY *next;
   HISCORE_ENTRY *prev;
};

struct hiscore
{
   char *keyword;
   char *desc;
   int vnum;
   short max_length;
   short length;
   HISCORE_ENTRY *first_entry;   /* first entry is number 1, highest score */
   HISCORE_ENTRY *last_entry; /* last entry is number 10 or last, lowest */

   HISCORE *next;
   HISCORE *prev;
};

extern HISCORE *first_table;
extern HISCORE *last_table;

/* Temporary Functions... Easier to weed out the stupid crap */
void free_all(  )
{
   int x;
   long i;
   MORPH_DATA *morph, *morph_next;
   BOARD_DATA *board, *board_next;
   NOTE_DATA *note, *note_next;
   SOCIALTYPE *social, *social_next;
   CHAR_DATA *ch, *ch_next;
   HELP_DATA *help, *help_next;
   CMDTYPE *cmd, *cmd_next;
   ROOM_INDEX_DATA *room;
   DESCRIPTOR_DATA *desc, *desc_next;
   SKILLTYPE *skill;
   HISCORE *score, *score_next;
   PLANE_DATA *plane, *plane_next;
   WATCH_DATA *watch, *watch_next;
   struct race_type *race;
   OBJ_INDEX_DATA *obj;
   MOB_INDEX_DATA *mob;
   SHOP_DATA *shop, *shop_next;
   int sn;

   for( desc = first_descriptor; desc; desc = desc_next )
   {
      desc_next = desc->next;
      if( desc->character )
      {
         //extract_char( ch, TRUE );
         desc->character->desc = NULL;
         desc->character = NULL;
      }
      UNLINK( desc, first_descriptor, last_descriptor, next, prev );
      free_desc( desc );
   }
   log_string( "Done Descriptor" );

   for( ch = first_char; ch; ch = ch_next )
   {
      ch_next = ch->next;
      //        if( IS_NPC( ch ) )
      if( ch != supermob )
      {
         extract_char( ch, TRUE );
         UNLINK( ch, first_char, last_char, next, prev );
         free_char( ch );
      }
   }
   clean_char_queue(  );

   log_string( "Done Character List" );
   for( x = 0; x <= 126; x++ )
   {
      for( cmd = command_hash[x]; cmd; cmd = cmd_next )
      {
         cmd_next = cmd->next;
         unlink_command( cmd );
         free_command( cmd );
      }
   }
   log_string( "Done Commands" );
   for( help = first_help; help; help = help_next )
   {
      help_next = help->next;
      STRFREE( help->text );
      STRFREE( help->keyword );
      DISPOSE( help );
   }
   log_string( "Done Helps" );
   for( morph = morph_start; morph; morph = morph_next )
   {
      morph_next = morph->next;
      free_morph( morph );
   }
   log_string( "Done Morphs" );
   for( x = 0; x < 27; x++ )
      for( social = social_index[x]; social; social = social_next )
      {
         social_next = social->next;
         unlink_social( social );
         free_social( social );
      }
   log_string( "Done Socials" );
   for( board = first_board; board; board = board_next )
   {
      board_next = board->next;
      for( note = board->first_note; note; note = note_next )
      {
         note_next = note->next;
         free_note( note );
      }
      DISPOSE( board->note_file );
      DISPOSE( board->read_group );
      DISPOSE( board->post_group );
      DISPOSE( board->extra_readers );
      DISPOSE( board->extra_removers );
      DISPOSE( board );
   }
   log_string( "Done Boards" );
   close_all_areas(  );
   log_string( "Done Areas" );
   for( i = 1; i <= 60300; i++ )
   {
      bool found = FALSE;
      room = get_room_index( i );
      if( room )
      {
         for( ch = room->first_person; ch; ch = ch->next )
            if( ch == supermob )
               found = TRUE;
         if( !found )
            delete_room( room );
      }
   }
   log_string( "Done Rooms" );
   free_hpk_list(  );
   log_string( "Done High PK List" );

   for( sn = 0; sn < top_sn; sn++ )
   {
      if( !skill_table[sn]->name )
         break;

      skill = skill_table[sn];
      DISPOSE( skill->name );

      if( skill->affects )
      {
         SMAUG_AFF *aff, *aff_next;

         for( aff = skill->affects; aff; aff = aff_next )
         {
            aff_next = aff->next;
            DISPOSE( aff->duration );
            DISPOSE( aff->modifier );
            DISPOSE( aff );
         }
      }
      DISPOSE( skill->components );
      DISPOSE( skill->noun_damage );
      DISPOSE( skill->dice );
      if( skill->die_char )
         DISPOSE( skill->die_char );
      if( skill->die_room )
         DISPOSE( skill->die_room );
      if( skill->die_vict )
         DISPOSE( skill->die_vict );
      DISPOSE( skill->hit_char );
      DISPOSE( skill->hit_dest );
      DISPOSE( skill->hit_room );
      DISPOSE( skill->hit_vict );
      DISPOSE( skill->imm_char );
      DISPOSE( skill->imm_room );
      DISPOSE( skill->imm_vict );
      DISPOSE( skill->miss_char );
      DISPOSE( skill->miss_room );
      DISPOSE( skill->miss_vict );
      DISPOSE( skill->teachers );
      DISPOSE( skill->msg_off );
      DISPOSE( skill );
   }
   log_string( "Done Skills" );

   for( score = first_table; score; score = score_next )
   {
      score_next = score->next;
      destroy_hitable( score->keyword );
   }
   log_string( "Done HiScores Table" );

   for( plane = first_plane; plane; plane = plane_next )
   {
      plane_next = plane->next;
      UNLINK( plane, first_plane, last_plane, next, prev );
      STRFREE( plane->name );
      DISPOSE( plane );
      check_planes( plane );
   }
   log_string( "Done Planes" );

   for( watch = first_watch; watch; watch = watch_next )
   {
      watch_next = watch->next;
      /*
       * Oops someone forgot to clear up the memory --Shaddai 
       */
      if( watch->imm_name )
         DISPOSE( watch->imm_name );
      if( watch->player_site )
         DISPOSE( watch->player_site );
      if( watch->target_name )
         DISPOSE( watch->target_name );
      /*
       * Now we can unlink and then clear up that final
       * * pointer -- Shaddai
       */
      UNLINK( watch, first_watch, last_watch, next, prev );
      DISPOSE( watch );
      return;
   }
   log_string( "Done watch stuff" );
   for( x = 0; x < MAX_RACE; x++ )
   {
      race = race_table[x];

      for( i = 0; i <= MAX_WHERE_NAME; i++ )
      {
         if( race->where_name[i] )
            DISPOSE( race->where_name[i] );
      }
      DISPOSE( race );
   }
   log_string( "Done Races" );
   for( x = 0; x < 5000; x++ )
   {
      obj = get_obj_index( x );
      delete_obj( obj );
   }
   log_string( "Done Objects" );
   for( x = 0; x < 5000; x++ )
   {
      mob = get_mob_index( x );
      delete_mob( mob );
   }
   log_string( "Done Mob" );

   DISPOSE( auction );
   log_string( "Done auction" );

   for( shop = first_shop; shop; shop = shop_next )
   {
      shop_next = shop->next;

      DISPOSE( shop );
   }
   log_string( "Done Shops" );

   for( x = 0; x < MAX_CLASS; x++ )
   {
      struct class_type *class;

      class = class_table[x];

      if( !class || !class->who_name )
         continue;

      STRFREE( class->who_name );
      for( i = 0; i <= MAX_LEVEL; i++ )
      {
         if( title_table[x][i][0] )
            DISPOSE( title_table[x][i][0] );
         if( title_table[x][i][1] )
            DISPOSE( title_table[x][i][1] );
      }
      DISPOSE( class );
   }
}

bool exists_file( char *fname );

void open_mud_log( void )
{
   FILE *error_log;
   char buf[MAX_INPUT_LENGTH];
   int logindex;

   for( logindex = 1000;; logindex++ )
   {
      sprintf( buf, "../log/%d.log", logindex );
      if( exists_file( buf ) )
         continue;
      else
         break;
   }

   if( !( error_log = fopen( buf, "a" ) ) )
   {
      fprintf( stderr, "Unable to append to %s.", buf );
      exit( 1 );
   }

   dup2( fileno( error_log ), STDERR_FILENO );
   fclose( error_log );
   error_log = NULL;
}

#ifdef WIN32
int mainthread( int argc, char **argv )
#else
int main( int argc, char **argv )
#endif
{
   struct timeval now_time;
   char hostn[128];
   bool fCopyOver = FALSE;
#ifdef IMC
   int imcsocket = -1;
#endif

   num_descriptors = 0;
   first_descriptor = NULL;
   last_descriptor = NULL;
   sysdata.NO_NAME_RESOLVING = TRUE;
   sysdata.WAIT_FOR_AUTH = TRUE;

   /*
    * Init time.
    */
   gettimeofday( &now_time, NULL );
   current_time = ( time_t ) now_time.tv_sec;
   /*
    * gettimeofday( &boot_time, NULL);   okay, so it's kludgy, sue me :) 
    */
   boot_time = time( 0 );  /*  <-- I think this is what you wanted */
   strcpy( str_boot_time, ctime( &current_time ) );

   /*
    * Init boot time.
    */
   set_boot_time = &set_boot_time_struct;
   set_boot_time->manual = 0;

   new_boot_time = update_time( localtime( &current_time ) );
   /*
    * Copies *new_boot_time to new_boot_struct, and then points
    * new_boot_time to new_boot_struct again. -- Alty 
    */
   new_boot_struct = *new_boot_time;
   new_boot_time = &new_boot_struct;
   new_boot_time->tm_mday += 1;  /* Should set reboots for every 1 days */
   if( new_boot_time->tm_hour > 12 )
      new_boot_time->tm_mday += 1;
   new_boot_time->tm_sec = 0;
   new_boot_time->tm_min = 0;
   new_boot_time->tm_hour = 6;

   /*
    * Update new_boot_time (due to day increment) 
    */
   new_boot_time = update_time( new_boot_time );
   new_boot_struct = *new_boot_time;
   new_boot_time = &new_boot_struct;
   /*
    * Bug fix submitted by Gabe Yoder 
    */
   new_boot_time_t = mktime( new_boot_time );
   reboot_check( mktime( new_boot_time ) );
   /*
    * Set reboot time string for do_time 
    */
   get_reboot_string(  );
   init_pfile_scan_time(  );  /* Pfile autocleanup initializer - Samson 5-8-99 */

   /*
    * Get the port number.
    */
   port = 4000;
   if( argc > 1 )
   {
      if( !is_number( argv[1] ) )
      {
         fprintf( stderr, "Usage: %s [port #]\n", argv[0] );
         exit( 1 );
      }
      else if( ( port = atoi( argv[1] ) ) <= 1024 )
      {
         fprintf( stderr, "Port number must be above 1024.\n" );
         exit( 1 );
      }
      if( argv[2] && argv[2][0] )
      {
         fCopyOver = TRUE;
         control = atoi( argv[3] );
#ifdef I3
         I3_socket = atoi( argv[4] );
#endif
#ifdef IMC
         imcsocket = atoi( argv[5] );
#endif
      }
      else
         fCopyOver = FALSE;
   }

   /*
    * If this all goes well, we should be able to open a new log file during hotboot 
    */
   if( fCopyOver )
   {
      open_mud_log(  );
      log_string( "Hotboot: Spawning new log file" );
   }

   /*
    * Run the game.
    */
#ifdef WIN32
   {
      /*
       * Initialise Windows sockets library 
       */

      unsigned short wVersionRequested = MAKEWORD( 1, 1 );
      WSADATA wsadata;
      int err;

      /*
       * Need to include library: wsock32.lib for Windows Sockets 
       */
      err = WSAStartup( wVersionRequested, &wsadata );
      if( err )
      {
         fprintf( stderr, "Error %i on WSAStartup\n", err );
         exit( 1 );
      }

      /*
       * standard termination signals 
       */
      signal( SIGINT, ( void * )bailout );
      signal( SIGTERM, ( void * )bailout );
   }
#endif /* WIN32 */

#ifdef I3
   /*
    * Initialize and connect to Intermud-3 
    */
   I3_main( FALSE, port, fCopyOver );
#endif

#ifdef IMC
   /*
    * Initialize and connect to IMC2 
    */
   imc_startup( FALSE, imcsocket, fCopyOver );
#endif

   log_string( "Booting Database" );
   boot_db( fCopyOver );

#ifdef ISPELL
   /*
    * Start Ispell -- Kratas 
    */
   log_string( "Loading ispell" );
   ispell_init(  );
#endif

   log_string( "Initializing socket" );
   if( !fCopyOver )  /* We have already the port if copyover'ed */
      control = init_socket( port );

#ifdef WEBSVR
   if( sysdata.webtoggle == TRUE )
   {
      init_web( sysdata.webport );
   }
#endif
   /*
    * I don't know how well this will work on an unnamed machine as I don't
    * have one handy, and the man pages are ever-so-helpful.. -- Alty 
    */
   if( gethostname( hostn, sizeof( hostn ) ) < 0 )
   {
      perror( "main: gethostname" );
      strcpy( hostn, "unresolved" );
   }
   sprintf( log_buf, "%s ready at address %s on port %d.", sysdata.mud_name, hostn, port );

   log_string( log_buf );

   game_loop(  );

   /*
    * Save the world's position -- Scion 
    */
   {
      CHAR_DATA *vch;

      save_world( NULL );
      for( vch = first_char; vch; vch = vch->next )
      {
         if( !IS_NPC( vch ) )
         {
            save_char_obj( vch );
         }
      }
   }

#ifdef WEBSVR
   if( sysdata.webtoggle == TRUE )
   {
      shutdown_web(  );
   }
#endif
   close( control );

#ifdef I3
   I3_shutdown( 0 );
#endif

#ifdef IMC
   imc_shutdown( FALSE );
#endif

#ifdef WIN32
   if( service_shut_down )
   {
      CHAR_DATA *vch;

      /*
       * Save all characters before booting. 
       */
      for( vch = first_char; vch; vch = vch->next )
         if( !IS_NPC( vch ) )
         {
            shutdown_checkpoint(  );
            save_char_obj( vch );
         }
   }
   /*
    * Shut down Windows sockets 
    */

   WSACleanup(  );   /* clean up */
   kill_timer(  );   /* stop timer thread */
#endif

#ifdef ISPELL
   /*
    * Shutdown ispell 
    */
   ispell_done(  );
#endif

//    free_all();
   log_string( "Saving game world time...." );
   save_timedata(  );

   /*
    * That's all, folks.
    */
   log_string( "Normal termination of game." );
   exit( 0 );
   return 0;
}


int init_socket( int mport )
{
   char hostname[64];
   struct sockaddr_in sa;
   int x = 1;
   int fd;

   gethostname( hostname, sizeof( hostname ) );


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

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

#if defined(SO_DONTLINGER) && !defined(SYSV)
   {
      struct linger ld;

      ld.l_onoff = 1;
      ld.l_linger = 1000;

      if( setsockopt( fd, SOL_SOCKET, SO_DONTLINGER, ( void * )&ld, sizeof( ld ) ) < 0 )
      {
         perror( "Init_socket: SO_DONTLINGER" );
         close( fd );
         exit( 1 );
      }
   }
#endif

   memset( &sa, '\0', sizeof( sa ) );
   sa.sin_family = AF_INET;
   sa.sin_port = htons( mport );

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

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

   return fd;
}

/*
 * LAG alarm!							-Thoric
 * Fixed little alarm_section bug in the sprintf -- Kratas
 */
void caught_alarm(  )
{
   char buf[MAX_STRING_LENGTH];

   sprintf( buf, "ALARM CLOCK!  In section %s", alarm_section );
   bug( buf );
   strcpy( buf, "Alas, the hideous malevalent entity known only as 'Lag' rises once more!\r\n" );
   echo_to_all( AT_IMMORT, buf, ECHOTAR_ALL );
   if( newdesc )
   {
      FD_CLR( newdesc, &in_set );
      FD_CLR( newdesc, &out_set );
      FD_CLR( newdesc, &exc_set );
      log_string( "clearing newdesc" );
   }
}

bool check_bad_desc( int desc )
{
   if( FD_ISSET( desc, &exc_set ) )
   {
      FD_CLR( desc, &in_set );
      FD_CLR( desc, &out_set );
      log_string( "Bad FD caught and disposed." );
      return TRUE;
   }
   return FALSE;
}

/*
 * Determine whether this player is to be watched  --Gorog
 */
bool chk_watch( short player_level, char *player_name, char *player_site )
{
   WATCH_DATA *pw;
/*
    char buf[MAX_INPUT_LENGTH];
    sprintf( buf, "che_watch entry: plev=%d pname=%s psite=%s",
                  player_level, player_name, player_site);
    log_string(buf);
*/
   if( !first_watch )
      return FALSE;

   for( pw = first_watch; pw; pw = pw->next )
   {
      if( pw->target_name )
      {
         if( !str_cmp( pw->target_name, player_name ) && player_level < pw->imm_level )
            return TRUE;
      }
      else if( pw->player_site )
      {
         if( !str_prefix( pw->player_site, player_site ) && player_level < pw->imm_level )
            return TRUE;
      }
   }
   return FALSE;
}


void accept_new( int ctrl )
{
   static struct timeval null_time;
   DESCRIPTOR_DATA *d;
   /*
    * int maxdesc; Moved up for use with id.c as extern 
    */

   /*
    * Poll all active descriptors.
    */
   FD_ZERO( &in_set );
   FD_ZERO( &out_set );
   FD_ZERO( &exc_set );
   FD_SET( ctrl, &in_set );
   maxdesc = ctrl;
   newdesc = 0;
   for( d = first_descriptor; d; d = d->next )
   {
      maxdesc = UMAX( maxdesc, d->descriptor );
      FD_SET( d->descriptor, &in_set );
      FD_SET( d->descriptor, &out_set );
      FD_SET( d->descriptor, &exc_set );
      if( d == last_descriptor )
         break;
   }

   if( select( maxdesc + 1, &in_set, &out_set, &exc_set, &null_time ) < 0 )
   {
      perror( "accept_new: select: poll" );
      exit( 1 );
   }

   if( FD_ISSET( ctrl, &exc_set ) )
   {
      bug( "Exception raise on controlling descriptor %d", ctrl );
      FD_CLR( ctrl, &in_set );
      FD_CLR( ctrl, &out_set );
   }
   else if( FD_ISSET( ctrl, &in_set ) )
   {
      newdesc = ctrl;
      new_descriptor( newdesc );
   }
}

void game_loop(  )
{
   struct timeval last_time;
   int last_check = 0;
   char cmdline[MAX_INPUT_LENGTH];
   DESCRIPTOR_DATA *d;
/*  time_t	last_check = 0;  */

#ifndef WIN32
   signal( SIGPIPE, SIG_IGN );
   signal( SIGALRM, caught_alarm );
#endif

   /*
    * signal( SIGSEGV, SegVio ); 
    */
   gettimeofday( &last_time, NULL );
   current_time = ( time_t ) last_time.tv_sec;

   /*
    * Main loop 
    */
   while( !mud_down )
   {
      accept_new( control );
      /*
       * Looks like sombody forgot to put this in, might be
       * the cause of some troubles
       *          --Callidyrr
       */
#ifdef WEBSVR
      if( sysdata.webtoggle == TRUE )
      {
         handle_web(  );
      }
#endif

      /*
       * Kick out descriptors with raised exceptions
       * or have been idle, then check for input.
       */
      for( d = first_descriptor; d; d = d_next )
      {
         if( d == d->next )
         {
            bug( "descriptor_loop: loop found & fixed" );
            d->next = NULL;
         }
         d_next = d->next;

         d->idle++;  /* make it so a descriptor can idle out */
         if( FD_ISSET( d->descriptor, &exc_set ) )
         {
            FD_CLR( d->descriptor, &in_set );
            FD_CLR( d->descriptor, &out_set );
            if( d->character
                && ( d->connected == CON_PLAYING
                     || d->connected == CON_EDITING || ( d->connected >= CON_NOTE_TO && d->connected <= CON_NOTE_FINISH ) ) )
               save_char_obj( d->character );
            d->outtop = 0;
            close_socket( d, TRUE );
            continue;
         }
         else if( ( !d->character && d->idle > 360 )  /* 2 mins */
                  || ( d->connected != CON_PLAYING && d->idle > 1200 )  /* 5 mins */
                  || d->idle > 28800 ) /* 2 hrs  */
         {
            write_to_descriptor( d, "Idle timeout... disconnecting.\r\n", 0 );
            d->outtop = 0;
            close_socket( d, TRUE );
            continue;
         }
         else
         {
            d->fcommand = FALSE;

            if( FD_ISSET( d->descriptor, &in_set ) )
            {
               d->idle = 0;
               if( d->character )
                  d->character->timer = 0;
               if( !read_from_descriptor( d ) )
               {
                  FD_CLR( d->descriptor, &out_set );
                  if( d->character
                      && ( d->connected == CON_PLAYING
                           || d->connected == CON_EDITING
                           || ( d->connected >= CON_NOTE_TO && d->connected <= CON_NOTE_FINISH ) ) )
                     save_char_obj( d->character );
                  d->outtop = 0;
                  close_socket( d, FALSE );
                  continue;
               }
            }

            if( d->character && d->character->wait > 0 )
            {
               --d->character->wait;
               continue;
            }

            /*
             * Check for running aliases -- Scion 
             */
            if( d->connected == CON_PLAYING && d->character->pcdata && d->character->pcdata->alias_queue != NULL )
            {
               ALIAS_QUEUE *temp;

               temp = d->character->pcdata->alias_queue;
               interpret( d->character, d->character->pcdata->alias_queue->cmd );
               d->character->pcdata->alias_queue = d->character->pcdata->alias_queue->next;

               DISPOSE( temp );

               d->character->pcdata->alias_used++;
               if( d->character->pcdata->alias_used >= 20 )
               {
                  d->character->pcdata->alias_queue = NULL;
                  d->character->pcdata->alias_used = 0;
                  send_to_char( "Infinite loop detected... Your alias has been terminated.\r\n", d->character );
               }
               continue;
            }

            read_from_buffer( d );
            if( d->incomm[0] != '\0' )
            {
               d->fcommand = TRUE;
               stop_idling( d->character );

               strcpy( cmdline, d->incomm );
               d->incomm[0] = '\0';

               if( d->character )
                  set_cur_char( d->character );

               if( d->character )
               {
                  if( IS_NPC( d->character ) )
                     sprintf( lastplayercmd, "(%d)%s, in room %d, used %s",
                              d->character->pIndexData->vnum, d->character->name, d->character->in_room ?
                              d->character->in_room->vnum : 0, cmdline );
                  else
                     sprintf( lastplayercmd, "%s, in room %d, used %s",
                              d->character->name, d->character->in_room ? d->character->in_room->vnum : 0, cmdline );
               }
               if( d->pagepoint )
                  set_pager_input( d, cmdline );
               else
                  switch ( d->connected )
                  {
                     default:
                        nanny( d, cmdline );
                        break;
                     case CON_PLAYING:
                        if( d->character && !IS_NPC( d->character ) )
                           d->character->pcdata->alias_used = 0;
                        interpret( d->character, cmdline );
                        break;
                     case CON_EDITING:
                        edit_buffer( d->character, cmdline );
                        break;
                     case CON_NOTE_TEXT:
                        handle_con_note_text( d->character, cmdline );
                        break;
                  }
            }
         }
         if( d == last_descriptor )
            break;
      }

      /*
       * imud_recv_message(); 
       */

/* Local server InterMUD channel - mudmsg.c */

#ifdef I3
      I3_loop(  );
#endif

#ifdef IMC
      imc_loop(  );
#endif

      /*
       * Autonomous game motion.
       */
      update_handler(  );

      /*
       * Check REQUESTS pipe
       */
      check_requests(  );

      /*
       * Output.
       */
      for( d = first_descriptor; d; d = d_next )
      {
         d_next = d->next;

         if( ( d->fcommand || d->outtop > 0 || d->pagetop > 0 ) && FD_ISSET( d->descriptor, &out_set ) )
         {
            if( d->pagepoint )
            {
               if( !pager_output( d ) )
               {
                  if( d->character
                      && ( d->connected == CON_PLAYING
                           || d->connected == CON_EDITING
                           || ( d->connected >= CON_NOTE_TO && d->connected <= CON_NOTE_FINISH ) ) )
                     save_char_obj( d->character );
                  d->outtop = 0;
                  close_socket( d, FALSE );
               }
            }
            else if( !flush_buffer( d, TRUE ) )
            {
               if( d->character
                   && ( d->connected == CON_PLAYING
                        || d->connected == CON_EDITING
                        || ( d->connected >= CON_NOTE_TO && d->connected <= CON_NOTE_FINISH ) ) )
                  save_char_obj( d->character );
               d->outtop = 0;
               close_socket( d, FALSE );
            }
         }
         if( d == last_descriptor )
            break;
      }

      /*
       * Synchronize to a clock.
       * Sleep( last_time + 1/PULSE_PER_SECOND - now ).
       * Careful here of signed versus unsigned arithmetic.
       */
      {
         struct timeval now_time;
         long secDelta;
         long usecDelta;

         gettimeofday( &now_time, NULL );
         usecDelta = ( ( int )last_time.tv_usec ) - ( ( int )now_time.tv_usec ) + 1000000 / PULSE_PER_SECOND;
         secDelta = ( ( int )last_time.tv_sec ) - ( ( int )now_time.tv_sec );
         while( usecDelta < 0 )
         {
            usecDelta += 1000000;
            secDelta -= 1;
         }

         while( usecDelta >= 1000000 )
         {
            usecDelta -= 1000000;
            secDelta += 1;
         }

         if( secDelta > 0 || ( secDelta == 0 && usecDelta > 0 ) )
         {
            struct timeval stall_time;

            stall_time.tv_usec = usecDelta;
            stall_time.tv_sec = secDelta;
#ifdef WIN32
            Sleep( ( stall_time.tv_sec * 1000L ) + ( stall_time.tv_usec / 1000L ) );
#else
            if( select( 0, NULL, NULL, NULL, &stall_time ) < 0 && errno != EINTR )
            {
               perror( "game_loop: select: stall" );
               exit( 1 );
            }
#endif
         }
      }

      gettimeofday( &last_time, NULL );
      current_time = ( time_t ) last_time.tv_sec;

      /*
       * Check every 10 seconds...  
       */
      if( last_check + 10 < current_time )
      {
         CHECK_LINKS( first_descriptor, last_descriptor, next, prev, DESCRIPTOR_DATA );
         last_check = current_time;
      }

   }
   /*
    * Save morphs so can sort later. --Shaddai 
    */
   if( sysdata.morph_opt )
      save_morphs(  );

   fflush( stderr ); /* make sure strerr is flushed */
   return;
}

void init_descriptor( DESCRIPTOR_DATA * dnew, int desc )
{
   dnew->next = NULL;
   dnew->descriptor = desc;
   dnew->connected = CON_ANSI;   /* Josh */
   dnew->outsize = 2000;
   dnew->idle = 0;
   dnew->lines = 0;
   dnew->scrlen = 24;
   dnew->user = STRALLOC( "unknown" );
   dnew->newstate = 0;
   dnew->prevcolor = 0x07;

   CREATE( dnew->outbuf, char, dnew->outsize );
}


void new_descriptor( int new_desc )
{
   char buf[MAX_STRING_LENGTH];
   DESCRIPTOR_DATA *dnew;
   struct sockaddr_in sock;
   struct hostent *from;
   int desc;
   size_t size;
   char bugbuf[MAX_STRING_LENGTH];
#ifdef WIN32
   unsigned long arg = 1;
#endif

   size = sizeof( sock );
   if( check_bad_desc( new_desc ) )
   {
      set_alarm( 0 );
      return;
   }
   set_alarm( 20 );
   alarm_section = "new_descriptor::accept";
   if( ( desc = accept( new_desc, ( struct sockaddr * )&sock, &size ) ) < 0 )
   {
      perror( "New_descriptor: accept" );
      sprintf( bugbuf, "[*****] BUG: New_descriptor: accept" );
      log_string_plus( bugbuf, LOG_COMM, sysdata.log_level );
      set_alarm( 0 );
      return;
   }
   if( check_bad_desc( new_desc ) )
   {
      set_alarm( 0 );
      return;
   }
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

   set_alarm( 20 );
   alarm_section = "new_descriptor: after accept";

#ifdef WIN32
   if( ioctlsocket( desc, FIONBIO, &arg ) == -1 )
#else
   if( fcntl( desc, F_SETFL, FNDELAY ) == -1 )
#endif
   {
      perror( "New_descriptor: fcntl: FNDELAY" );
      set_alarm( 0 );
      return;
   }
   if( check_bad_desc( new_desc ) )
      return;

   CREATE( dnew, DESCRIPTOR_DATA, 1 );

   init_descriptor( dnew, desc );

   dnew->port = ntohs( sock.sin_port );

   strcpy( buf, inet_ntoa( sock.sin_addr ) );
   sprintf( log_buf, "Sock.sinaddr:  %s, port %hd.", buf, dnew->port );
   log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
   if( sysdata.NO_NAME_RESOLVING )
      dnew->host = STRALLOC( buf );
   else
   {
      from = gethostbyaddr( ( char * )&sock.sin_addr, sizeof( sock.sin_addr ), AF_INET );
      dnew->host = STRALLOC( ( char * )( from ? from->h_name : buf ) );
   }

   if( check_total_bans( dnew ) )
   {
      write_to_descriptor( dnew, "Your site has been banned from this Mud.\r\n", 0 );
      free_desc( dnew );
      set_alarm( 0 );
      return;
   }

   /*
    * Init descriptor data.
    */

   if( !last_descriptor && first_descriptor )
   {
      DESCRIPTOR_DATA *d;

      bug( "New_descriptor: last_desc is NULL, but first_desc is not! ...fixing" );
      for( d = first_descriptor; d; d = d->next )
         if( !d->next )
            last_descriptor = d;
   }

   LINK( dnew, first_descriptor, last_descriptor, next, prev );

#ifdef MCCP
   write_to_buffer( dnew, eor_on_str, 0 );
   write_to_buffer( dnew, compress_on_str, 0 );
#endif


   /*
    * lets limit how many connecions a player may have and set a limit
    * on how many pkillers a player can have loaded up at once 
    * this one mainly will kick them out of they have more then the mplimit 
    * since at this point the d->character is still NULL 
    */
   if( is_multiplaying( dnew ) == TRUE )
   {
      write_to_buffer( dnew, "You are above your multiplaying limit.\r\n", 0 );
      write_to_buffer( dnew, "Try again after you quit some of the characters.\r\n", 0 );
      close_socket( dnew, FALSE );
      /*
       * Never know might need this here if it causes memory leaks later 
       */
//       free_desc(dnew);
      set_alarm( 0 );
      return;
   }

   /*
    * Send the greeting.
    */
   sprintf( buf, "Welcome to %s, would you like ANSI color? [Y/N] ", sysdata.mud_name );
   write_to_buffer( dnew, buf, 0 ); /* -- Kratas */

   if( ++num_descriptors > sysdata.maxplayers )
   {
      sysdata.maxplayers = num_descriptors;
      sysdata.totalplayers = num_descriptors;
   }
   if( sysdata.maxplayers > sysdata.alltimemax )
   {
      if( sysdata.time_of_max )
         DISPOSE( sysdata.time_of_max );
      sprintf( buf, "%24.24s", ctime( &current_time ) );
      sysdata.time_of_max = str_dup( buf );
      sysdata.alltimemax = sysdata.maxplayers;
      sprintf( log_buf, "Broke all-time maximum player record: %d", sysdata.alltimemax );
      log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
      to_channel( log_buf, "Monitor", LEVEL_IMMORTAL );
      save_sysdata( sysdata );
   }
   set_alarm( 0 );
   return;
}

void free_desc( DESCRIPTOR_DATA * d )
{
   close( d->descriptor );
   STRFREE( d->host );
   DISPOSE( d->outbuf );
   STRFREE( d->user );  /* identd */
   if( d->pagebuf )
      DISPOSE( d->pagebuf );
#ifdef MCCP
   compressEnd( d );
#endif
   DISPOSE( d );
/*    --num_descriptors;  This is called from more than close_socket -- Alty */
   return;
}

void close_socket( DESCRIPTOR_DATA * dclose, bool force )
{
   CHAR_DATA *ch;
   DESCRIPTOR_DATA *d;
   bool DoNotUnlink = FALSE;

   /*
    * flush outbuf 
    */
   if( !force && dclose->outtop > 0 )
      flush_buffer( dclose, FALSE );

   /*
    * say bye to whoever's snooping this descriptor 
    */
   if( dclose->snoop_by )
      write_to_buffer( dclose->snoop_by, "Your victim has left the game.\r\n", 0 );

   /*
    * stop snooping everyone else 
    */
   for( d = first_descriptor; d; d = d->next )
      if( d->snoop_by == dclose )
         d->snoop_by = NULL;

   /*
    * Check for switched people who go link-dead. -- Altrag 
    */
   if( dclose->original )
   {
      if( ( ch = dclose->character ) != NULL )
         do_return( ch, "" );
      else
      {
         bug( "Close_socket: dclose->original without character %s",
              ( dclose->original->name ? dclose->original->name : "unknown" ) );
         dclose->character = dclose->original;
         dclose->original = NULL;
      }
   }

   ch = dclose->character;

   /*
    * sanity check :( 
    */
   if( !dclose->prev && dclose != first_descriptor )
   {
      DESCRIPTOR_DATA *dp, *dn;
      bug( "Close_socket: %s desc:%p != first_desc:%p and desc->prev = NULL!",
           ch ? ch->name : d->host, dclose, first_descriptor );
      dp = NULL;
      for( d = first_descriptor; d; d = dn )
      {
         dn = d->next;
         if( d == dclose )
         {
            bug( "Close_socket: %s desc:%p found, prev should be:%p, fixing.", ch ? ch->name : d->host, dclose, dp );
            dclose->prev = dp;
            break;
         }
         dp = d;
      }
      if( !dclose->prev )
      {
         bug( "Close_socket: %s desc:%p could not be found!.", ch ? ch->name : dclose->host, dclose );
         DoNotUnlink = TRUE;
      }
   }
   if( !dclose->next && dclose != last_descriptor )
   {
      DESCRIPTOR_DATA *dp, *dn;
      bug( "Close_socket: %s desc:%p != last_desc:%p and desc->next = NULL!",
           ch ? ch->name : d->host, dclose, last_descriptor );
      dn = NULL;
      for( d = last_descriptor; d; d = dp )
      {
         dp = d->prev;
         if( d == dclose )
         {
            bug( "Close_socket: %s desc:%p found, next should be:%p, fixing.", ch ? ch->name : d->host, dclose, dn );
            dclose->next = dn;
            break;
         }
         dn = d;
      }
      if( !dclose->next )
      {
         bug( "Close_socket: %s desc:%p could not be found!.", ch ? ch->name : dclose->host, dclose );
         DoNotUnlink = TRUE;
      }
   }

   if( dclose->character )
   {
      drop_artifacts( ch, ch->last_carrying );
      sprintf( log_buf, "Closing link to %s.", ch->pcdata->filename );
      log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
/*
	if ( ch->level < LEVEL_DEMI )
	  to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
*/
      if( ( dclose->connected == CON_PLAYING
            || dclose->connected == CON_EDITING )
          || ( dclose->connected >= CON_NOTE_TO && dclose->connected <= CON_NOTE_FINISH ) )
/*
        if ( dclose->connected == CON_PLAYING
	||   dclose->connected == CON_EDITING ) */
      {
         act( AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_CANSEE );
         ch->desc = NULL;
      }
      else if( dclose->connected == CON_OEDIT || dclose->connected == CON_REDIT || dclose->connected == CON_MEDIT )
      {
         act( AT_ACTION, "$n has lost $s link.", ch, NULL, NULL, TO_CANSEE );
         ch->desc = NULL;
      }
      else
      {
         /*
          * clear descriptor pointer to get rid of bug message in log 
          */
         dclose->character->desc = NULL;
         free_char( dclose->character );
      }
   }


   if( !DoNotUnlink )
   {
      /*
       * make sure loop doesn't get messed up 
       */
      if( d_next == dclose )
         d_next = d_next->next;
      UNLINK( dclose, first_descriptor, last_descriptor, next, prev );
   }

#ifdef MCCP
   compressEnd( dclose );
#endif

   if( dclose->descriptor == maxdesc )
      --maxdesc;

   free_desc( dclose );
   --num_descriptors;
   return;
}

bool read_from_descriptor( DESCRIPTOR_DATA * d )
{
   int iErr;
   size_t iStart;

   /*
    * Hold horses if pending command already. 
    */
   if( d->incomm[0] != '\0' )
      return TRUE;

   /*
    * Check for overflow. 
    */
   iStart = strlen( d->inbuf );
   if( iStart >= sizeof( d->inbuf ) - 10 )
   {
      sprintf( log_buf, "%s input overflow!", d->host );
      log_string( log_buf );
      write_to_descriptor( d,
                           "\r\n*** PUT A LID ON IT!!! ***\r\nYou cannot enter the same command more than 20 consecutive times!\r\n",
                           0 );
      return FALSE;
   }

   for( ;; )
   {
      int nRead;

      nRead = recv( d->descriptor, d->inbuf + iStart, sizeof( d->inbuf ) - 10 - iStart, 0 );
#ifdef WIN32
      iErr = WSAGetLastError(  );
#else
      iErr = errno;
#endif
      if( nRead > 0 )
      {
         iStart += nRead;
         if( d->inbuf[iStart - 1] == '\n' || d->inbuf[iStart - 1] == '\r' )
            break;
      }
      else if( nRead == 0 )
      {
         log_string_plus( "EOF encountered on read.", LOG_COMM, sysdata.log_level );
         return FALSE;
      }
      else if( iErr == EWOULDBLOCK )
         break;
      else
      {
         perror( "Read_from_descriptor" );
         return FALSE;
      }
   }

   d->inbuf[iStart] = '\0';
   return TRUE;
}



/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer( DESCRIPTOR_DATA * d )
{
   int i, j, k;
   int iac = 0;

   /*
    * Hold horses if pending command already.
    */
   if( d->incomm[0] != '\0' )
      return;

   /*
    * Look for at least one new line.
    */
   for( i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r' && i < MAX_INBUF_SIZE; i++ )
   {
      if( d->inbuf[i] == '\0' )
         return;
   }

   /*
    * Canonical input processing.
    */
   for( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
   {
      if( k >= 508 ) // Increased size of max line length.
      {
         write_to_descriptor( d, "Line too long.\r\n", 0 );

         /*
          * skip the rest of the line 
          */
         /*
          * for ( ; d->inbuf[i] != '\0' || i>= MAX_INBUF_SIZE ; i++ )
          * {
          * if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
          * break;
          * }
          */
         d->inbuf[i] = '\n';
         d->inbuf[i + 1] = '\0';
         break;
      }
#ifdef MCCP
      if( d->inbuf[i] == ( signed char )IAC )
         iac = 1;
      else if( iac == 1 && ( d->inbuf[i] == ( signed char )DO || d->inbuf[i] == ( signed char )DONT ) )
         iac = 2;
      else if( iac == 2 )
      {
         iac = 0;
         if( d->inbuf[i] == ( signed char )TELOPT_COMPRESS )
         {
            if( d->inbuf[i - 1] == ( signed char )DO )
               compressStart( d );
            else if( d->inbuf[i - 1] == ( signed char )DONT )
               compressEnd( d );
         }
      }
      else
#endif
      if( d->inbuf[i] == '\b' && k > 0 )
         --k;
      else if( isascii( d->inbuf[i] ) && isprint( d->inbuf[i] ) )
         d->incomm[k++] = d->inbuf[i];
   }

   /*
    * Finish off the line.
    */
   if( k == 0 )
      d->incomm[k++] = ' ';
   d->incomm[k] = '\0';

   /*
    * Deal with bozos with #repeat 1000 ...
    */
   if( k > 1 || d->incomm[0] == '!' )
   {
      if( d->incomm[0] != '!' && strcmp( d->incomm, d->inlast ) )
      {
         d->repeat = 0;
      }
      else
      {
         /*
          * Was 20, now 40 -- Kratas 
          */
         if( ++d->repeat >= 40 )
         {
/*		sprintf( log_buf, "%s input spamming!", d->host );
		log_string( log_buf );
*/
            write_to_descriptor( d,
                                 "\r\n*** PUT A LID ON IT!!! ***\r\nYou cannot enter the same command more than 20 consecutive times!\r\n",
                                 0 );
            strcpy( d->incomm, "quit" );
         }
      }
   }

   /*
    * Do '!' substitution.
    */
   if( d->incomm[0] == '!' && d->incomm[1] == '!' )
   {
      strcpy( d->incomm, d->incomm + 2 );
      strcat( d->incomm, " " );
      strcat( d->incomm, d->inlast );
   }
   else if( d->incomm[0] == '!' )
      strcpy( d->incomm, d->inlast );
   else
      strcpy( d->inlast, d->incomm );

   /*
    * Shift the input buffer.
    */
   while( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
      i++;
   for( j = 0; ( d->inbuf[j] = d->inbuf[i + j] ) != '\0'; j++ )
      ;
   return;
}

/*
 * Low level output function.
 */
bool flush_buffer( DESCRIPTOR_DATA * d, bool fPrompt )
{
   char buf[MAX_INPUT_LENGTH];

   /*
    * If buffer has more than 4K inside, spit out .5K at a time   -Thoric
    */
   if( !mud_down && d->outtop > 4096 )
   {
      memcpy( buf, d->outbuf, 512 );
      memmove( d->outbuf, d->outbuf + 512, d->outtop - 512 );
      d->outtop -= 512;
      if( d->snoop_by )
      {
         char snoopbuf[MAX_INPUT_LENGTH];

         buf[512] = '\0';
         if( d->character && d->character->name )
         {
            if( d->original && d->original->name )
               sprintf( snoopbuf, "%s (%s)", d->character->name, d->original->name );
            else
               sprintf( snoopbuf, "%s", d->character->name );
            write_to_buffer( d->snoop_by, snoopbuf, 0 );
         }
         write_to_buffer( d->snoop_by, "% ", 2 );
         write_to_buffer( d->snoop_by, buf, 0 );
      }
      if( !write_to_descriptor( d, buf, 512 ) )
      {
         d->outtop = 0;
         return FALSE;
      }
      return TRUE;
   }


   /*
    * Bust a prompt.
    */
   if( fPrompt && !mud_down && d->connected == CON_PLAYING )
   {
      CHAR_DATA *ch;

      ch = d->original ? d->original : d->character;
      if( xIS_SET( ch->act, PLR_BLANK ) )
         write_to_buffer( d, "\r\n", 2 );


      if( xIS_SET( ch->act, PLR_PROMPT ) )
         display_prompt( d );
      if( xIS_SET( ch->act, PLR_TELNET_GA ) )
         write_to_buffer( d, go_ahead_str, 0 );
   }

   /*
    * Short-circuit if nothing to write.
    */
   if( d->outtop == 0 )
      return TRUE;

   /*
    * Snoop-o-rama.
    */
   if( d->snoop_by )
   {
      /*
       * without check, 'force mortal quit' while snooped caused crash, -h 
       */
      if( d->character && d->character->name )
      {
         /*
          * Show original snooped names. -- Altrag 
          */
         if( d->original && d->original->name )
            sprintf( buf, "%s (%s)", d->character->name, d->original->name );
         else
            sprintf( buf, "%s", d->character->name );
         write_to_buffer( d->snoop_by, buf, 0 );
      }
      write_to_buffer( d->snoop_by, "% ", 2 );
      write_to_buffer( d->snoop_by, d->outbuf, d->outtop );
   }

   /*
    * OS-dependent output.
    */
   if( !write_to_descriptor( d, d->outbuf, d->outtop ) )
   {
      d->outtop = 0;
      return FALSE;
   }
   else
   {
      d->outtop = 0;
      return TRUE;
   }
}

/*
 * Append onto an output buffer.
 */
void write_to_buffer( DESCRIPTOR_DATA * d, const char *txt, size_t length )
{
   if( !d )
   {
      bug( "Write_to_buffer: NULL descriptor" );
      return;
   }

   /*
    * Normally a bug... but can happen if loadup is used.
    */
   if( !d->outbuf )
      return;

   /*
    * Find length in case caller didn't.
    */
   if( length <= 0 )
      length = strlen( txt );

   /*
    * Initial \r\n if needed.
    */
   if( d->outtop == 0 && !d->fcommand )
   {
      d->outbuf[0] = '\r';
      d->outbuf[1] = '\n';
      d->outtop = 2;
   }

   /*
    * Expand the buffer as needed.
    */
   while( d->outtop + length >= d->outsize )
   {
      if( d->outsize > 32000 )
      {
         /*
          * empty buffer 
          */
         d->outtop = 0;
         close_socket( d, TRUE );
         bug( "Buffer overflow. Closing (%s).", d->character ? d->character->name : "???" );
         return;
      }
      d->outsize *= 2;
      RECREATE( d->outbuf, char, d->outsize );
   }

   /*
    * Copy.
    */
   strncpy( d->outbuf + d->outtop, txt, length );
   d->outtop += length;
   d->outbuf[d->outtop] = '\0';
   return;
}

/*
 * Lowest level output function.
 * Write a block of text to the file descriptor.
 * If this gives errors on very long blocks (like 'ofind all'),
 *   try lowering the max block size.
 */
bool write_to_descriptor2( int desc, char *txt, int length )
{
   int iStart;
   int nWrite;
   int nBlock;

   if( length <= 0 )
      length = strlen( txt );

   for( iStart = 0; iStart < length; iStart += nWrite )
   {
      nBlock = UMIN( length - iStart, 4096 );
      if( ( nWrite = send( desc, txt + iStart, nBlock, 0 ) ) < 0 )
      {
         perror( "Write_to_descriptor" );
         return FALSE;
      }
   }

   return TRUE;
}

bool write_to_descriptor( DESCRIPTOR_DATA * d, char *txt, int length )
{
#ifdef MCCP
   if( d->out_compress )
      return writeCompressed( d, txt, length );
   else
      return write_to_descriptor2( d->descriptor, txt, length );
#else
   return write_to_descriptor2( d->descriptor, txt, length );
#endif
}

void show_title( DESCRIPTOR_DATA * d )
{
   CHAR_DATA *ch;

   ch = d->character;

   if( !IS_SET( ch->pcdata->flags, PCFLAG_NOINTRO ) )
   {
      if( xIS_SET( ch->act, PLR_RIP ) )
         send_rip_title( ch );
      else if( xIS_SET( ch->act, PLR_ANSI ) )
         send_ansi_title( ch );
      else
         send_ascii_title( ch );
   }
   else
   {
      write_to_buffer( d, "Press enter...\r\n", 0 );
   }
   d->connected = CON_PRESS_ENTER;
   if( !IS_IMMORTAL( ch ) )
   {
   }
}

bool hair_length( DESCRIPTOR_DATA *, bool, RACE_TYPE *, char );
bool eye_color( DESCRIPTOR_DATA *, bool, RACE_TYPE *, char );
bool skin_tone( DESCRIPTOR_DATA *, bool, RACE_TYPE *, char );
bool char_build( DESCRIPTOR_DATA *, bool, RACE_TYPE *, char );
bool char_height( DESCRIPTOR_DATA *, bool, RACE_TYPE *, char );

/*
 * Modified a bit from orig design so can use with online race editing
 * (and creation). Someone else can figure out how to set this up with
 * online race editing later. Also, consider making the colors editable
 * online too! All functions below relating to race attributes, refer to
 * this comment.
 * -- Kratas
 */
bool hair_color( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *hair_colors[] = {
      "Black", "White", "Gray", "Brown", "Light Brown", "Dark Brown",
      "Blond", "Light Blond", "Dark Blond", "Golden Blond", "Brown Blond",
      "Red", "Red Brown", "Red Blond", "Blood Red", "Orange Red",
      "Silver", "Strawberry", "White", NULL
   };

   if( !str_cmp( d->character->pcdata->hair_length, "Bald" ) )
   {
      d->character->pcdata->hair_color = STRALLOC( "Non-Existant" );
      d->connected = CON_GET_EYE_COLOR;
      eye_color( d, FALSE, race, '\0' );
      return TRUE;
   }

   if( !question )
   {
      write_to_buffer( d, "\r\nSelect a Hair Color:\r\n", 0 );
      buf = buffer_new( 200 );

      for( x = 0, y = 'A'; hair_colors[x]; x++, y++ )
         if( IS_SET( race->attribs[HAIR_COLOR], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, hair_colors[x] );

      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[HAIR_COLOR], 1 << x ) )
   {
      d->character->pcdata->hair_color = STRALLOC( hair_colors[x] );
      d->connected = CON_GET_EYE_COLOR;
      eye_color( d, FALSE, race, '\0' );
      return TRUE;
   }

   return FALSE;
}

bool hair_length( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *hair_lengths[] = {
      "Bald", "Crew-Cut", "Short", "Medium", "Long", "Waist", NULL
   };

   if( !question )
   {
      buf = buffer_new( 200 );
      write_to_buffer( d, "\r\nSelect a Hair length:\r\n", 0 );

      for( x = 0, y = 'A'; hair_lengths[x]; x++, y++ )
         if( IS_SET( race->attribs[HAIR_LENGTH], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, hair_lengths[x] );

      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[HAIR_LENGTH], 1 << x ) )
   {
      d->character->pcdata->hair_length = STRALLOC( hair_lengths[x] );
      d->connected = CON_GET_HAIR_COLOR;
      hair_color( d, FALSE, race, '\0' );
      return TRUE;
   }

   return FALSE;
}

bool eye_color( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *eye_colors[] = {
      "Aqua", "Brown", "Green", "Teal", "Blue-Green", "Hazel", "Blue",
      "Hazel-Blue", "Hazel-Green", "Light Brown", "Baby Blue",
      "Jade", "Red", "Blood-Red", "Silver", NULL
   };

   if( !question )
   {
      buf = buffer_new( 200 );

      write_to_buffer( d, "\r\nSelect an eye color:\r\n", 0 );
      for( x = 0, y = 'A'; eye_colors[x]; x++, y++ )
         if( IS_SET( race->attribs[EYE_COLOR], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, eye_colors[x] );

      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[EYE_COLOR], 1 << x ) )
   {
      d->character->pcdata->eye_color = STRALLOC( eye_colors[x] );
      d->connected = CON_GET_SKIN_TONE;
      skin_tone( d, FALSE, race, '\0' );
      return TRUE;
   }

   return FALSE;
}

bool skin_tone( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *skin_tones[] = {
      "Brown", "Pale", "Red", "White", "Yellow", "Tan",
      "Green", "Blue", "Gold", "Silver", "Bronze", "Copper", "Gray", NULL
   };

   if( !question )
   {
      buf = buffer_new( 200 );
      write_to_buffer( d, "\r\nSelect a skin tone:\r\n", 0 );
      for( x = 0, y = 'A'; skin_tones[x]; x++, y++ )
      {
         if( IS_SET( race->attribs[SKIN_TONE], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, skin_tones[x] );
      }
      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[SKIN_TONE], 1 << x ) )
   {
      d->character->pcdata->skin_tone = STRALLOC( skin_tones[x] );
      d->connected = CON_GET_BUILD;
      char_build( d, FALSE, race, '\0' );
      return TRUE;
   }

   return FALSE;
}

bool char_build( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *builds[] = {
      "Bone", "Thin", "Medium", "Large", "Buff", "Hardy", NULL
   };

   if( !question )
   {
      buf = buffer_new( 200 );
      write_to_buffer( d, "\r\nSelect a Build:\r\n", 0 );
      for( x = 0, y = 'A'; builds[x]; x++, y++ )
      {
         if( IS_SET( race->attribs[RACE_BUILD], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, builds[x] );
      }
      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[RACE_BUILD], 1 << x ) )
   {
      d->character->pcdata->ch_build = STRALLOC( builds[x] );
      d->connected = CON_GET_HEIGHT;
      char_height( d, FALSE, race, '\0' );
      return TRUE;
   }

   return FALSE;
}

bool char_height( DESCRIPTOR_DATA * d, bool question, RACE_TYPE * race, char answer )
{
   int x, y;
   BUFFER *buf;
   char *heights[] = {
      "Midget", "Short", "Medium", "Tall", "Towering", NULL
   };

   if( !question )
   {
      buf = buffer_new( 200 );
      write_to_buffer( d, "\r\nSelect a Height:\r\n", 0 );
      for( x = 0, y = 'A'; heights[x]; x++, y++ )
      {
         if( IS_SET( race->attribs[RACE_HEIGHT], 1 << x ) )
            bprintf( buf, "(%c) %s\r\n", ( char )y, heights[x] );
      }
      write_to_buffer( d, buf->data, 0 );
      write_to_buffer( d, "Enter your selection: ", 0 );

      buffer_free( buf );
      return TRUE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   if( !isalpha( answer ) )
   {
      write_to_buffer( d, "Please type a letter.", 0 );
      return FALSE;
   }

   x = toupper( answer ) - 'A';  /* Gets Number */

   if( IS_SET( race->attribs[RACE_HEIGHT], 1 << x ) )
   {
      d->character->pcdata->ch_height = STRALLOC( heights[x] );
      return TRUE;
   }

   return FALSE;
}

char *clean_message( char *str )
{
   static char cleanstring[1000];
   int x = 0;

   strcpy( cleanstring, "" );

   for( ;; )
   {
      if( *str == '\0' )
      {
         cleanstring[x] = *str;
         return &cleanstring[0];
      }
      if( *str != '\r' )
      {
         cleanstring[x] = *str;
         str++;
         x++;
      }
      else
         str++;
   }
   return NULL;
}

void send_email( char *subject, char *email, char *message, CHAR_DATA * ch )
{
   static char sendstring[1000];
   FILE *fp;
   FILE *mfp;
#define MAIL_ROOT_DIR "/bin/mail"

   strcpy( sendstring, "" );

   message = clean_message( message ); //gets rid of the /r in the message if there are any..
   fp = fopen( EMAIL_FILE, "w" );
   fprintf( fp, "%s", message );
   fclose( fp );
   //sprintf(sendstring, "mail -s \"%s\"  \"%s\" < %s", subject, email, EMAIL_FILE);
   //system(&sendstring[0]);
   sprintf( sendstring, "%s -s \"%s\"  \"%s\" < %s", MAIL_ROOT_DIR, subject, email, EMAIL_FILE );
   if( ( mfp = popen( sendstring, "w" ) ) == NULL )
   {
      if( ch )
         send_to_char( "The message was not sent because the mail program could not be found.\r\n", ch );
      bug( "send_email:  Could not location mail." );
      return;
   }
   pclose( mfp );
   remove( EMAIL_FILE );
   if( ch )
      ch_printf( ch, "Your email has been sent to %s\r\n", email );
}

int check_email_syntax( char *arg1, char *arg2 )
{
   int x;

   for( x = 0;; x++ )
   {
      if( arg1[x] == 34 || arg1[x] == 39 )   // a " and a '
         return FALSE;
      if( arg1[x] == '\0' )
         break;
   }
   for( x = 0;; x++ )
   {
      if( arg2[x] == 34 || arg2[x] == 39 )   // a " and a '
         return FALSE;
      if( arg2[x] == '\0' )
         break;
   }
   return TRUE;
}

void do_sendmail( CHAR_DATA * ch, char *argument )
{
   char arg1[100];
   char arg2[100];
   static char *passargument;

   if( ch->dest_buf )
   {
      argument = ch->dest_buf;
   }
   else
   {
      passargument = "";
   }

   passargument = argument;

   if( IS_NPC( ch ) )
   {
      send_to_char( "Huh?\r\n", ch );
      return;
   }

   if( argument[0] == '\0' )
   {
      send_to_char( "Syntax:  sendmail \"<subject>\" \"<recepient's email address>\"\r\n", ch );
      send_to_char( "Once that is typed, you will be sent into the buffer.\r\n", ch );
      return;
   }
   argument = one_argument( argument, arg1 );
   argument = one_argument( argument, arg2 );

   if( arg1[0] == '\0' )
   {
      send_to_char( "You need to supply a subject.\r\n", ch );
      return;
   }
   if( arg2[0] == '\0' )
   {
      send_to_char( "You need to supply an email address.\r\n", ch );
      return;
   }

   if( strlen( arg1 ) > 95 )
   {
      send_to_char( "Subject cannot be longer than 95 characters.\r\n", ch );
      return;
   }
   if( strlen( arg1 ) > 95 )
   {
      send_to_char( "recepient's email address cannot be longer than 95 characters.\r\n", ch );
      return;
   }

   if( !check_email_syntax( arg1, arg2 ) )
   {
      send_to_char( "You cannot supply any \" or \' in your subject or recepient.\r\n", ch );
      return;
   }

   switch ( ch->substate )
   {
      default:
         bug( "do_description: illegal substate", 0 );
         return;

      case SUB_RESTRICTED:
         send_to_char( "You cannot use this command from within another command.\r\n", ch );
         return;

      case SUB_NONE:
         if( ch->pcdata->sendmail )
            STRFREE( ch->pcdata->sendmail );
         ch->pcdata->sendmail = STRALLOC( "" );
         ch->substate = SUB_WRITING_EMAIL;
         ch->dest_buf = passargument;
         start_editing( ch, ch->pcdata->sendmail );
         return;

      case SUB_WRITING_EMAIL:
         STRFREE( ch->pcdata->sendmail );
         passargument = "";
         ch->pcdata->sendmail = copy_buffer( ch );
         send_email( &arg1[0], &arg2[0], ch->pcdata->sendmail, ch );
         bug( "-------------%s is sending an email to %s-------------\r\n", ch->name, arg2 );
         stop_editing( ch );
         return;
   }
}

/*
 * Deal with sockets that haven't logged in yet.
 */
void nanny( DESCRIPTOR_DATA * d, char *argument )
{
   int i;
   char buf[MAX_STRING_LENGTH];
   char arg[MAX_STRING_LENGTH];
   CHAR_DATA *ch;
   char *pwdnew;
   char *p;
   int iClass;
   int iRace;
   int value;
   char closenum[MSL];  /* Main Menu quit fix, all i've done is marked with a -G- -Garinan */
   bool fOld, chk;
   RACE_TYPE *race;

   while( isspace( *argument ) )
      argument++;

   ch = d->character;
   sprintf( closenum, "%d", d->descriptor ); /* -G- */

   switch ( d->connected )
   {
      default:
         bug( "Nanny: bad d->connected %d.", d->connected );
         close_socket( d, TRUE );
         return;

      case CON_ANSI:   /* Josh */
         if( argument[0] == '\0' || UPPER( argument[0] ) == 'Y' )
         {
            CHAR_DATA *tmp;

            CREATE( tmp, CHAR_DATA, 1 );
            d->character = tmp;
            tmp->desc = d;
            xSET_BIT( tmp->act, PLR_ANSI );
            d->ansi = TRUE;
            send_to_char_color( "&RAnsi enabled!&w\r\n", d->character );
            d->connected = CON_GET_NAME;
            {
               extern char *help_greeting;
               if( help_greeting[0] == '.' )
                  send_to_char_color( help_greeting + 1, d->character );
               else
                  send_to_char_color( help_greeting, d->character );
            }
            tmp->desc = NULL;
            d->character = NULL;
            DISPOSE( tmp );
            tmp = NULL;
            return;
         }
         else if( UPPER( argument[0] ) == 'N' )
         {
            d->ansi = FALSE;
            write_to_buffer( d, "Ansi disabled!\r\n", 0 );
            d->connected = CON_GET_NAME;
            {
               extern char *help_greeting2;
               if( help_greeting2[0] == '.' )
                  write_to_buffer( d, help_greeting2 + 1, 0 );
               else
                  write_to_buffer( d, help_greeting2, 0 );
            }
            return;
         }
         else
         {
            write_to_buffer( d, "Do you want ANSI color? [Y/N] ", 0 );
            return;
         }

      case CON_OEDIT:
         oedit_parse( d, argument );
         break;

      case CON_REDIT:
         redit_parse( d, argument );
         break;

      case CON_MEDIT:
         medit_parse( d, argument );
         break;

      case CON_GET_NAME:
         if( argument[0] == '\0' )
         {
            close_socket( d, FALSE );
            return;
         }

         argument[0] = UPPER( argument[0] );

         /*
          * Old players can keep their characters. -- Alty 
          */
         if( !check_parse_name( argument, ( d->newstate != 0 ) ) )
         {
            write_to_buffer( d, "Illegal name, try another.\r\nName: ", 0 );
            return;
         }

         if( !str_cmp( argument, "New" ) )
         {
            if( d->newstate == 0 )
            {
               /*
                * New player 
                * Don't allow new players if DENY_NEW_PLAYERS is true 
                */
               if( sysdata.DENY_NEW_PLAYERS == TRUE )
               {
                  sprintf( buf, "The mud is currently preparing for a reboot.\r\n" );
                  write_to_buffer( d, buf, 0 );
                  sprintf( buf, "New players are not accepted during this time.\r\n" );
                  write_to_buffer( d, buf, 0 );
                  sprintf( buf, "Please try again in a few minutes.\r\n" );
                  write_to_buffer( d, buf, 0 );
                  close_socket( d, FALSE );
               }
               sprintf( buf, "\r\nPlease choose a name for your character: " );
               write_to_buffer( d, buf, 0 );
               d->newstate++;
               d->connected = CON_GET_NAME;
               return;
            }
            else
            {
               write_to_buffer( d, "Illegal name, try another.\r\nName: ", 0 );
               return;
            }
         }

         if( check_playing( d, argument, FALSE ) == BERR )
         {
            write_to_buffer( d, "Name: ", 0 );
            return;
         }

         fOld = load_char_obj( d, argument, TRUE );
         if( !d->character )
         {
            sprintf( log_buf, "Bad player file %s@%s.", argument, d->host );
            log_string( log_buf );
            write_to_buffer( d, "Your playerfile is corrupt...Please notify ucmm@ucmm.dhs.org.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }
         ch = d->character;
         if( check_bans( ch, BAN_SITE ) )
         {
            write_to_buffer( d, "Your site has been banned from this Mud.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }

         if( fOld )
         {
            if( check_bans( ch, BAN_CLASS ) )
            {
               write_to_buffer( d, "Your class has been banned from this Mud.\r\n", 0 );
               close_socket( d, FALSE );
               return;
            }
            if( check_bans( ch, BAN_RACE ) )
            {
               write_to_buffer( d, "Your race has been banned from this Mud.\r\n", 0 );
               close_socket( d, FALSE );
               return;
            }
         }

         if( xIS_SET( ch->act, PLR_DENY ) )
         {
            sprintf( log_buf, "Denying access to %s@%s.", argument, d->host );
            log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
            if( d->newstate != 0 )
            {
               write_to_buffer( d, "That name is already taken.  Please choose another: ", 0 );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }
            write_to_buffer( d, "You are denied access.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }

         /*
          *  Make sure the immortal host is from the correct place.
          *  Shaddai
          */
         if( IS_IMMORTAL( ch ) && sysdata.check_imm_host && !check_immortal_domain( ch, d->host ) )
         {
            sprintf( log_buf, "%s's char being hacked from %s.", argument, d->host );
            log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
            write_to_buffer( d, "This hacking attempt has been logged.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }

         chk = check_reconnect( d, argument, FALSE );
         if( chk == BERR )
            return;

         if( chk )
         {
            fOld = TRUE;
         }
         else
         {
            if( sysdata.wizlock && !IS_IMMORTAL( ch ) )
            {
               write_to_buffer( d, "The game is wizlocked.  Only immortals can connect now.\r\n", 0 );
               write_to_buffer( d, "Please try back later.\r\n", 0 );
               close_socket( d, FALSE );
               return;
            }
         }

         if( fOld )
         {
            if( d->newstate != 0 )
            {
               write_to_buffer( d, "That name is already taken.  Please choose another: ", 0 );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }
            /*
             * Old player
             */
            write_to_buffer( d, "Password: ", 0 );
            write_to_buffer( d, echo_off_str, 0 );
            d->connected = CON_GET_OLD_PASSWORD;
            return;
         }
         else
         {
            if( d->newstate == 0 )
            {
               /*
                * No such player 
                */
               write_to_buffer( d,
                                "\r\nNo such player exists.\r\nPlease check your spelling, or type new to start a new player.\r\n\r\nName: ",
                                0 );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }

            sprintf( buf, "Did I get that right, %s (Y/N)? ", argument );
            write_to_buffer( d, buf, 0 );
            STRFREE( ch->name );
            ch->name = STRALLOC( argument );
            d->connected = CON_CONFIRM_NEW_NAME;
            return;
         }
         break;

      case CON_GET_OLD_PASSWORD:
         write_to_buffer( d, "\r\n", 2 );

         if( strcmp( crypt( argument, ch->pcdata->pwd ), ch->pcdata->pwd ) )
         {
            write_to_buffer( d, "Wrong password.\r\n", 0 );
            /*
             * clear descriptor pointer to get rid of bug message in log 
             */
            d->character->desc = NULL;
            close_socket( d, FALSE );
            return;
         }

         write_to_buffer( d, echo_on_str, 0 );

         if( check_playing( d, ch->pcdata->filename, TRUE ) )
            return;

         /*
          * if ( check_login( d, ch->pcdata->filename ) )
          * return; 
          */

         chk = check_reconnect( d, ch->pcdata->filename, TRUE );
         if( chk == BERR )
         {
            if( d->character && d->character->desc )
               d->character->desc = NULL;
            close_socket( d, FALSE );
            return;
         }
         if( chk == TRUE )
            return;

         /*
          * lets limit how many connecions a player may have and set a limit
          * on how many pkillers a player can have loaded up at once 
          */
         if( is_multiplaying( d ) == TRUE )
         {
            write_to_buffer( d, "You are above your multiplaying limit.\r\n", 0 );
            write_to_buffer( d, "Try again after you quit some of the characters.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }

         strcpy( buf, ch->pcdata->filename );
         d->character->desc = NULL;
         free_char( d->character );
         d->character = NULL;
         fOld = load_char_obj( d, buf, FALSE );
         ch = d->character;
         if( ch->position == POS_FIGHTING
             || ch->position == POS_EVASIVE
             || ch->position == POS_DEFENSIVE || ch->position == POS_AGGRESSIVE || ch->position == POS_BERSERK )
            ch->position = POS_STANDING;

         sprintf( log_buf, "%s@%s(%s) has connected.", ch->pcdata->filename, d->host, d->user );
         sysdata.totalplayers += 1;
         if( ch->level < LEVEL_DEMI )
         {
            /*
             * to_channel( log_buf, CHANNEL_MONITOR, "Monitor", ch->level );
             */
            log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
         }
         else
            log_string_plus( log_buf, LOG_COMM, ch->level );
         {
            struct tm *tme;
            time_t now;
            char day[50];
            now = time( 0 );
            tme = localtime( &now );
            strftime( day, 50, "%a %b %d %H:%M:%S %Y", tme );
            sprintf( log_buf, "%-20s     %-24s    %s", ch->pcdata->filename, day, d->host );
            write_last_file( log_buf );
         }
         /* i need this for debugging - shogar */
         to_channel( log_buf, "Monitor", 113 );
         show_title( d );
         break;

      case CON_CONFIRM_NEW_NAME:
         switch ( *argument )
         {
            case 'y':
            case 'Y':
               sprintf( buf, "\r\nMake sure to use a password that won't be easily guessed by someone else."
                        "\r\nPick a good password for %s: %s", ch->name, echo_off_str );
               write_to_buffer( d, buf, 0 );
               d->connected = CON_GET_NEW_PASSWORD;
               break;

            case 'n':
            case 'N':
               write_to_buffer( d, "Ok, what IS it, then? ", 0 );
               /*
                * clear descriptor pointer to get rid of bug message in log 
                */
               d->character->desc = NULL;
               free_char( d->character );
               d->character = NULL;
               d->connected = CON_GET_NAME;
               break;

            default:
               write_to_buffer( d, "Please type Yes or No. ", 0 );
               break;
         }
         break;

      case CON_GET_NEW_PASSWORD:
         write_to_buffer( d, "\r\n", 2 );

         if( strlen( argument ) < 5 )
         {
            write_to_buffer( d, "Password must be at least five characters long.\r\nPassword: ", 0 );
            return;
         }

         pwdnew = crypt( argument, ch->name );
         for( p = pwdnew; *p != '\0'; p++ )
         {
            if( *p == '~' )
            {
               write_to_buffer( d, "New password not acceptable, try again.\r\nPassword: ", 0 );
               return;
            }
         }

         DISPOSE( ch->pcdata->pwd );
         ch->pcdata->pwd = str_dup( pwdnew );
         write_to_buffer( d, "\r\nPlease retype the password to confirm: ", 0 );
         d->connected = CON_CONFIRM_NEW_PASSWORD;
         break;

      case CON_CONFIRM_NEW_PASSWORD:
         write_to_buffer( d, "\r\n", 2 );

         if( strcmp( crypt( argument, ch->pcdata->pwd ), ch->pcdata->pwd ) )
         {
            write_to_buffer( d, "Passwords don't match.\r\nRetype password: ", 0 );
            d->connected = CON_GET_NEW_PASSWORD;
            return;
         }

         write_to_buffer( d, echo_on_str, 0 );
         write_to_buffer( d, "\r\nWhat is your sex (M/F/N)? ", 0 );
         d->connected = CON_GET_NEW_SEX;
         break;

         /*
          * All switch statements below are now simplified a bit. -- Kratas 
          */
      case CON_GET_NEW_SEX:
         switch ( UPPER( argument[0] ) )
         {
            case 'M':
               ch->sex = SEX_MALE;
               break;
            case 'F':
               ch->sex = SEX_FEMALE;
               break;
            case 'N':
               ch->sex = SEX_NEUTRAL;
               break;
            default:
               write_to_buffer( d, "That's not a sex.\r\nWhat IS your sex? ", 0 );
               return;
         }

         write_to_buffer( d, "\r\nSelect a class, or type help [class] to learn more about that class.\r\n[", 0 );
         buf[0] = '\0';

         for( iClass = 0; iClass < MAX_PC_CLASS; iClass++ )
         {
            if( class_table[iClass]->who_name && class_table[iClass]->who_name[0] != '\0' )
            {
               if( iClass > 0 )
               {
                  if( strlen( buf ) + strlen( class_table[iClass]->who_name ) > 77 )
                  {
                     strcat( buf, "\r\n" );
                     write_to_buffer( d, buf, 0 );
                     buf[0] = '\0';
                  }
                  else
                     strcat( buf, " " );
               }
               strcat( buf, class_table[iClass]->who_name );
            }
         }
         strcat( buf, "]\r\n: " );
         write_to_buffer( d, buf, 0 );
         d->connected = CON_GET_NEW_CLASS;
         break;

      case CON_GET_HAIR_COLOR:
         STRFREE( ch->pcdata->hair_color );

         race = race_table[d->character->race];
         switch ( hair_color( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not a hair color.\r\nWhat IS your hair color?", 0 );
               ch->pcdata->hair_color = STRALLOC( "" );
               return;

            case TRUE:
//            d->connected = CON_GET_EYE_COLOR;
//            eye_color( d, FALSE, race, '\0' );
               return;

            default:
               bug( "CON_GET_HAIR_COLOR, function hair_color(), is returning an erroneous value. Please repair." );
               break;
         }
         d->connected = CON_GET_EYE_COLOR;
         break;

      case CON_GET_HAIR_LENGTH:
         STRFREE( ch->pcdata->hair_length );

         race = race_table[d->character->race];
         switch ( hair_length( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not a hair length.\r\nWhat IS your hair length?", 0 );
               ch->pcdata->hair_length = STRALLOC( "" );
               return;

            case TRUE:
//            d->connected = CON_GET_HAIR_COLOR;
//            hair_color( d, FALSE, race, '\0' );
               return;

            default:
               bug( "CON_GET_HAIR_LENGTH, function hair_length(), is returning an erroneous value. Please repair." );
               break;
         }

         d->connected = CON_GET_HAIR_COLOR;
         break;

      case CON_GET_EYE_COLOR:
         STRFREE( ch->pcdata->eye_color );

         race = race_table[d->character->race];

         switch ( eye_color( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not an eye color.\r\nWhat IS your eye color?", 0 );
               ch->pcdata->eye_color = STRALLOC( "" );
               return;

            case TRUE:
//            d->connected = CON_GET_SKIN_TONE;
//            skin_tone( d, FALSE, race, '\0' );
               return;

            default:
               bug( "CON_GET_EYE_COLOR, function eye_color(), is returning an erroneous value. Please repair." );
               break;
         }
         d->connected = CON_GET_SKIN_TONE;
         break;

      case CON_GET_SKIN_TONE:
         STRFREE( ch->pcdata->skin_tone );

         race = race_table[d->character->race];
         switch ( skin_tone( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not a skin tone.\r\nWhat IS your skin tone?", 0 );
               ch->pcdata->skin_tone = STRALLOC( "" );
               return;

            case TRUE:
//            d->connected = CON_GET_BUILD;
//            char_build( d, FALSE, race, '\0' );
               return;

            default:
               bug( "CON_GET_SKIN_TONE, function skin_tone(), is returning an erroneous value. Please repair." );
               break;
         }
         d->connected = CON_GET_BUILD;
         break;

      case CON_GET_BUILD:
         STRFREE( ch->pcdata->ch_build );

         race = race_table[d->character->race];
         switch ( char_build( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not a build.\r\nWhat IS your build?", 0 );
               ch->pcdata->ch_build = STRALLOC( "" );
               return;

            case TRUE:
//            d->connected = CON_GET_HEIGHT;
//            char_height( d, FALSE, race, '\0' );
               return;

            default:
               bug( "CON_GET_BUILD, function char_build(), is returning an erroneous value. Please repair." );
               break;
         }

         d->connected = CON_GET_HEIGHT;
         break;

      case CON_GET_HEIGHT:
         STRFREE( ch->pcdata->ch_height );

         race = race_table[d->character->race];
         switch ( char_height( d, TRUE, race, argument[0] ) )
         {
            case FALSE:
               write_to_buffer( d, "That's not a height.\r\nWhat IS your height?", 0 );
               ch->pcdata->ch_height = STRALLOC( "" );
               return;

            case TRUE:
               d->connected = CON_GET_WANT_RIPANSI;
               write_to_buffer( d, "\r\nWould you like ANSI or no color support, (A/N)? ", 0 );
               return;

            default:
               bug( "CON_GET_HEIGHT, function char_height(), is returning an erroneous value. Please repair." );
               break;
         }

         write_to_buffer( d, "\r\nWould you like ANSI or no color support, (A/N)? ", 0 );
         d->connected = CON_GET_WANT_RIPANSI;
         break;

      case CON_GET_NEW_CLASS:
         argument = one_argument( argument, arg );

         if( !str_cmp( arg, "help" ) )
         {

            for( iClass = 0; iClass < MAX_CLASS; iClass++ )
            {
               if( toupper( argument[0] ) == toupper( class_table[iClass]->who_name[0] )
                   && !str_prefix( argument, class_table[iClass]->who_name ) )
               {
                  do_help( ch, argument );
                  write_to_buffer( d, "Please choose a class: ", 0 );
                  return;
               }
            }
            write_to_buffer( d, "No such help topic.  Please choose a class: ", 0 );
            return;
         }

         for( iClass = 0; iClass < MAX_CLASS; iClass++ )
         {
            if( toupper( arg[0] ) == toupper( class_table[iClass]->who_name[0] )
                && !str_prefix( arg, class_table[iClass]->who_name ) )
            {
               ch->class = iClass;
               break;
            }
         }

         if( iClass == MAX_CLASS || class_table[iClass]->who_name[0] == '\0' )
         {
            write_to_buffer( d, "That's not a class. \r\nWhat IS your class? ", 0 );
            return;
         }

         if( check_bans( ch, BAN_CLASS ) )
         {
            write_to_buffer( d, "That class is not currently avaiable.\r\nWhat IS your class? ", 0 );
            return;
         }

         write_to_buffer( d, "\r\nYou may choose from the following races, or type help [race] to learn more:\r\n[", 0 );
         buf[0] = '\0';
         for( iRace = 0; iRace < MAX_RACE; iRace++ )
         {
            if( race_table[iRace]->race_name && race_table[iRace]->race_name[0] != '\0'
                && !IS_SET( race_table[iRace]->class_restriction, 1 << ch->class )
                && str_cmp( race_table[iRace]->race_name, "unused" ) )
            {
               if( iRace > 0 )
               {
                  if( strlen( buf ) + strlen( race_table[iRace]->race_name ) > 77 )
                  {
                     strcat( buf, "\r\n" );
                     write_to_buffer( d, buf, 0 );
                     buf[0] = '\0';
                  }
                  else
                     strcat( buf, " " );
               }
               strcat( buf, race_table[iRace]->race_name );
            }
         }
         strcat( buf, "]\r\n: " );
         write_to_buffer( d, buf, 0 );
         d->connected = CON_GET_NEW_RACE;
         break;

      case CON_GET_NEW_RACE:
         argument = one_argument( argument, arg );
         if( !str_cmp( arg, "help" ) )
         {
            for( iRace = 0; iRace < MAX_RACE; iRace++ )
            {
               if( toupper( argument[0] ) == toupper( race_table[iRace]->race_name[0] )
                   && !str_prefix( argument, race_table[iRace]->race_name ) )
               {
                  do_help( ch, argument );
                  write_to_buffer( d, "Please choose a race: ", 0 );
                  return;
               }
            }
            write_to_buffer( d, "No help on that topic.  Please choose a race: ", 0 );
            return;
         }


         for( iRace = 0; iRace < MAX_RACE; iRace++ )
         {
            if( toupper( arg[0] ) == toupper( race_table[iRace]->race_name[0] )
                && !str_prefix( arg, race_table[iRace]->race_name ) )
            {
               ch->race = iRace;
               break;
            }
         }

         if( iRace == MAX_RACE
             || !race_table[iRace]->race_name || race_table[iRace]->race_name[0] == '\0'
             || IS_SET( race_table[iRace]->class_restriction, 1 << ch->class )
             || !str_cmp( race_table[iRace]->race_name, "unused" ) )
         {
            write_to_buffer( d, "That's not a race.\r\nWhat IS your race? ", 0 );
            return;
         }
         if( check_bans( ch, BAN_RACE ) )
         {
            write_to_buffer( d, "That race is not currently available.\r\nWhat is your race? ", 0 );
            return;
         }

         race = race_table[d->character->race];
         d->connected = CON_GET_HAIR_LENGTH;
         hair_length( d, FALSE, race, '\0' );
         /*
          * if( hair_color( d, FALSE, race, '\0' ); == 2 )
          * {
          * d->connected = CON_GET_HAIR_LENGTH;
          * hair_length( d, FALSE, race, '\0' );
          * return;
          * } 
          */
         break;

      case CON_GET_WANT_RIPANSI:
         switch ( argument[0] )
         {
            case 'a':
            case 'A':
               xSET_BIT( ch->act, PLR_ANSI );
               break;
            case 'n':
            case 'N':
               break;
            default:
               write_to_buffer( d, "Invalid selection.\r\nANSI or NONE? ", 0 );
               return;
         }
         sprintf( log_buf, "%s@%s new %s %s.", ch->name, d->host,
                  race_table[ch->race]->race_name, class_table[ch->class]->who_name );
         log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
         to_channel( log_buf, "Monitor", LEVEL_IMMORTAL );
         show_title( d );
         ch->level = 0;
         ch->position = POS_STANDING;
         d->connected = CON_PRESS_ENTER;  /* Change that to CON_PRESS_ENTER to show the motd -- Scion */
         return;
         break;

      case CON_PRESS_ENTER:
         /*
          * fixes 2nd , 3rd pager pages from not showing up - shogar 
          */
         set_pager_color( AT_GREY, ch );
         set_char_color( AT_GREY, ch );
         if( chk_watch( get_trust( ch ), ch->name, d->host ) ) /*  --Gorog */
            SET_BIT( ch->pcdata->flags, PCFLAG_WATCH );
         else
            REMOVE_BIT( ch->pcdata->flags, PCFLAG_WATCH );

         if( xIS_SET( ch->act, PLR_RIP ) )
            send_rip_screen( ch );
         if( xIS_SET( ch->act, PLR_ANSI ) )
            send_to_pager( "\033[2J", ch );
         else
            send_to_pager( "\014", ch );
         if( ch->level >= 104 )
            do_help( ch, "imotd" );
         else if( ch->level >= 101 )
            do_help( ch, "gmotd" );
         else if( ch->level >= 50 )
            do_help( ch, "amotd" );
         else if( ch->level >= 2 )
            do_help( ch, "motd" );
         else
            do_help( ch, "nmotd" );
         /*
          * lets limit how many connecions a player may have and set a limit
          * on how many pkillers a player can have loaded up at once 
          */
         if( is_multiplaying( d ) == TRUE )
         {
            write_to_buffer( d, "You are above your multiplaying limit.\r\n", 0 );
            write_to_buffer( d, "Try again after you quit some of the characters.\r\n", 0 );
            close_socket( d, FALSE );
            return;
         }
         send_to_pager( "\r\nPress [ENTER] ", ch );
         if( xIS_SET( ch->act, PLR_NOMENU ) || ( ch->level < 2 ) )
            d->connected = CON_READ_MOTD;
         else
            d->connected = CON_MAINMENU;
         break;
/* Scion */
      case CON_MAINMENU:
         if( ch->level < 2 )
         {
            d->connected = CON_PRESS_ENTER;  /* Skip mainmenu cause newbies can't see -- Scion */
            return;
            break;
         }
         if( xIS_SET( ch->act, PLR_ANSI ) )
            send_to_pager( "\033[2J", ch );
         else
            send_to_pager( "\014", ch );
         if( !xIS_SET( ch->act, PLR_NOMENU ) )
         {
            do_help( ch, "mainmenu" );
            write_to_buffer( d, "\r\nEnter your selection: ", 0 );
            d->connected = CON_MAINMENU2;
         }
         else
            d->connected = CON_READ_MOTD;
         return;
         break;

      case CON_MAINMENU2:
         argument = one_argument( argument, arg );
         value = is_number( arg ) ? atoi( arg ) : -1;

         if( atoi( arg ) < -1 && value == -1 )
            value = atoi( arg );

         switch ( value )
         {
            case 0: /* exit */
               write_to_buffer( d, "\r\nResortMUD awaits your return!\r\n", 0 );
               do_forceclose( NULL, closenum ); /* -G- */
               return;
               break;
            case 1: /* enter game */
               d->connected = CON_READ_MOTD;
               break;
            case 2: /* who list */
               do_who( ch, "" );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
            case 3: /* see wizlist */
               do_wizlist( ch, 0 );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;

            case 4:
               if( ch->level >= 104 )
                  do_help( ch, "imotd" );
               else if( ch->level >= 101 )
                  do_help( ch, "gmotd" );
               else if( ch->level >= 50 )
                  do_help( ch, "amotd" );
               else if( ch->level >= 2 )
                  do_help( ch, "motd" );
               else
                  do_help( ch, "nmotd" );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
            case 5: /* mkill record */
               do_hiscore( ch, "mkill" );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
            case 6: /* pkill record */
               do_hiscore( ch, "pkill" );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
            case 7: /* mdeath record */
               do_hiscore( ch, "mdeath" );
               write_to_buffer( d, "\r\nPress [ENTER] ", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
            default:
               write_to_buffer( d, "\r\nThat\'s not a menu choice!\r\n", 0 );
               d->connected = CON_MAINMENU;
               return;
               break;
         }
      case CON_READ_MOTD:
      {
         char motdbuf[MAX_STRING_LENGTH];

         sprintf( motdbuf, "\r\nWelcome to %s...\r\n", sysdata.mud_name );
         write_to_buffer( d, motdbuf, 0 );
      }
         add_char( ch );
         d->connected = CON_PLAYING;

         if( ch->level == 0 )
         {
            OBJ_DATA *obj;
            int iLang, uLang;

            /*
             * Set player birthday to current mud day, -17 years - Samson 10-25-99 
             */
            ch->day = time_info.day;
            ch->month = time_info.month;
            ch->year = time_info.year - 17;
            ch->age = 17;
            ch->age_bonus = 0;

            ch->pcdata->clan_name = STRALLOC( "" );
            ch->pcdata->clan = NULL;
            switch ( class_table[ch->class]->attr_prime )
            {
               case APPLY_STR:
                  ch->perm_str = 16;
                  break;
               case APPLY_INT:
                  ch->perm_int = 16;
                  break;
               case APPLY_WIS:
                  ch->perm_wis = 16;
                  break;
               case APPLY_DEX:
                  ch->perm_dex = 16;
                  break;
               case APPLY_CON:
                  ch->perm_con = 16;
                  break;
               case APPLY_CHA:
                  ch->perm_cha = 16;
                  break;
               case APPLY_LCK:
                  ch->perm_lck = 16;
                  break;
            }

            ch->perm_str += race_table[ch->race]->str_plus;
            ch->perm_int += race_table[ch->race]->int_plus;
            ch->perm_wis += race_table[ch->race]->wis_plus;
            ch->perm_dex += race_table[ch->race]->dex_plus;
            ch->perm_con += race_table[ch->race]->con_plus;
            ch->perm_cha += race_table[ch->race]->cha_plus;
            ch->affected_by = race_table[ch->race]->affected;
            ch->perm_lck += race_table[ch->race]->lck_plus;

            ch->armor += race_table[ch->race]->ac_plus;
            /*
             * Scion -- Average the two alignments. This way a drow paladin is
             * screwed (if the MUD is misguided enough to allow that combo) 
             */
            ch->alignment += ( race_table[ch->race]->alignment + class_table[ch->class]->alignment ) / 2;
            ch->attacks = race_table[ch->race]->attacks;
            ch->defenses = race_table[ch->race]->defenses;
            ch->saving_poison_death = race_table[ch->race]->saving_poison_death;
            ch->saving_wand = race_table[ch->race]->saving_wand;
            ch->saving_para_petri = race_table[ch->race]->saving_para_petri;
            ch->saving_breath = race_table[ch->race]->saving_breath;
            ch->saving_spell_staff = race_table[ch->race]->saving_spell_staff;

            ch->height = number_range( race_table[ch->race]->height * .9, race_table[ch->race]->height * 1.1 );
            ch->weight = number_range( race_table[ch->race]->weight * .9, race_table[ch->race]->weight * 1.1 );

            if( ( iLang = skill_lookup( "common" ) ) < 0 )
               bug( "Nanny: cannot find common language." );
            else
               ch->pcdata->learned[iLang] = 100;

            /*
             * Give them their racial languages 
             */
            if( race_table[ch->race] )
            {
               for( iLang = 0; lang_array[iLang] != LANG_UNKNOWN; iLang++ )
               {
                  if( IS_SET( race_table[ch->race]->language, 1 << iLang ) )
                  {
                     if( ( uLang = skill_lookup( lang_names[iLang] ) ) < 0 )
                        bug( "%s: cannot find racial language [%s].", __FUNCTION__, lang_names[iLang] );
                     else
                        ch->pcdata->learned[uLang] = 100;
                  }
               }
            }

            name_stamp_stats( ch );

            ch->level = 1;
            ch->exp = 0;
            ch->hit = ch->max_hit;
            ch->mana = ch->max_mana;
            ch->hit += race_table[ch->race]->hit;
            ch->mana += race_table[ch->race]->mana;
            ch->move = ch->max_move;

            if( ch->pcdata->clan )
               ch->pcdata->memorize[0] = ch->pcdata->clan->recall;
            else
               ch->pcdata->memorize[0] = ROOM_VNUM_TEMPLE;

            sprintf( buf, "the %s", title_table[ch->class][ch->level][ch->sex == SEX_FEMALE ? 1 : 0] );
            set_title( ch, buf );

            /*
             * Added by Narn.  Start new characters with autoexit and autgold
             * already turned on.  Very few people don't use those. 
             */
            xSET_BIT( ch->act, PLR_AUTOGOLD );
            xSET_BIT( ch->act, PLR_AUTOEXIT );
            ch->pcdata->chan_listen = STRALLOC( "chat auction quest music newbiechat guildtalk clantalk ordertalk ooc icc" );

            /*
             * Added by Brittany, Nov 24/96.  The object is the adventurer's guide
             * to the realms of despair, part of Academy.are. 
             */
            {
               OBJ_INDEX_DATA *obj_ind = get_obj_index( 10333 );
               if( obj_ind != NULL )
               {
                  obj = create_object( obj_ind, 0 );
                  obj_to_char( obj, ch );
                  equip_char( ch, obj, WEAR_HOLD );
               }
            }
            if( !sysdata.WAIT_FOR_AUTH )
               char_to_room( ch, get_room_index( ROOM_VNUM_SCHOOL ) );
            else
            {
               char_to_room( ch, get_room_index( ROOM_AUTH_START ) );
               ch->pcdata->auth_state = 0;
               SET_BIT( ch->pcdata->flags, PCFLAG_UNAUTHED );
            }
            /*
             * Display_prompt interprets blank as default 
             */
            ch->pcdata->prompt = STRALLOC( "" );
         }
         else if( !IS_IMMORTAL( ch ) && ch->pcdata->release_date > 0 && ch->pcdata->release_date > current_time )
         {
            if( ch->in_room->vnum == 6 )
               char_to_room( ch, ch->in_room );
            else
               char_to_room( ch, get_room_index( ROOM_VNUM_TEMPLE ) );
         }
         else if( ch->in_room && ( IS_IMMORTAL( ch ) || !IS_SET( ch->in_room->room_flags, ROOM_PROTOTYPE ) ) )
         {
            char_to_room( ch, ch->in_room );
         }
         else if( IS_IMMORTAL( ch ) )
         {
            char_to_room( ch, get_room_index( ROOM_VNUM_CHAT ) );
         }
         else
         {
            char_to_room( ch, get_room_index( ROOM_VNUM_TEMPLE ) );
         }

         if( get_timer( ch, TIMER_SHOVEDRAG ) > 0 )
            remove_timer( ch, TIMER_SHOVEDRAG );

         if( get_timer( ch, TIMER_PKILLED ) > 0 )
            remove_timer( ch, TIMER_PKILLED );

         ch->pcdata->outputprefix = NULL;
         ch->pcdata->outputsuffix = NULL;
         ch->pcdata->alias_queue = NULL;

         act( AT_ACTION, "$n has entered the realm.", ch, NULL, NULL, TO_CANSEE );

         if( !xIS_SET( ch->act, PLR_NEWMONEY ) )
         {
            send_to_char( "Adjusting your finances for the new money system.\r\n\r\n", ch );
            ch->gold = ( ch->gold / 5000 );
            xSET_BIT( ch->act, PLR_NEWMONEY );
         }

         sprintf( buf, "%s has entered the realm.", ch->name );

         /*
          * This should just do descriptor since it skips npcs anyway -- Kratas
          */
         {
            DESCRIPTOR_DATA *tmp;
            CHAR_DATA *wch;

            for( tmp = first_descriptor; tmp; tmp = tmp->next )
            {
               if( tmp->connected != CON_PLAYING )
                  continue;
               wch = tmp->original ? tmp->original : tmp->character;
               sprintf( buf, "&R[&WINFO&R]&c %s has entered the realm!\r\n", PERS( ch, wch ) );
               send_to_char_color( buf, wch );
            }
         }

         /*
          * Didn't see the point?
          * if (!IS_IMMORTAL(ch))
          * log_string_plus( buf, LOG_COMM, LEVEL_GREATER );
          * else
          * log_string_plus( buf, LOG_COMM, ch->level+1 );
          */

         if( ch->pcdata->pet )
         {
            act( AT_ACTION, "$n returns to $s master from the Void.", ch->pcdata->pet, NULL, ch, TO_NOTVICT );
            act( AT_ACTION, "$N returns with you to the realms.", ch, NULL, ch->pcdata->pet, TO_CHAR );
         }
         do_global_boards( ch, "" );
         send_to_char( "\r\n", ch );
         i = num_changes(  );
         if( i > 0 )
         {
            sprintf( buf,
                     "&R+++\r\n+++ &WThere were &C%d &d&Wchanges made to the mud today type &GCHANGES&W to view &R\r\n+++&D\r\n",
                     i );
            send_to_char( buf, ch );
         }
         do_look( ch, "auto" );
         tax_player( ch ); /* Here we go, let's tax players to lower the gold
                            * pool -- TRI */
         mail_count( ch );
         /*
          * update hometowns and nations -Nopey 
          */
         update_char_hometown( ch );
         check_loginmsg( ch );

         SET_BIT( ch->pcdata->flags, PCFLAG_IMMPROOF );

         if( !ch->was_in_room && ch->in_room == get_room_index( ROOM_VNUM_TEMPLE ) )
            ch->was_in_room = get_room_index( ROOM_VNUM_TEMPLE );
         else if( ch->was_in_room == get_room_index( ROOM_VNUM_TEMPLE ) )
            ch->was_in_room = get_room_index( ROOM_VNUM_TEMPLE );
         else if( !ch->was_in_room )
            ch->was_in_room = ch->in_room;
         break;

      case CON_NOTE_TO:
         handle_con_note_to( d, argument );
         break;

      case CON_NOTE_SUBJECT:
         handle_con_note_subject( d, argument );
         break;   /* subject */

      case CON_NOTE_EXPIRE:
         handle_con_note_expire( d, argument );
         break;

      case CON_NOTE_FINISH:
         handle_con_note_finish( d, argument );
         break;
   }

   return;
}

bool is_reserved_name( char *name )
{
   RESERVE_DATA *res;

   for( res = first_reserved; res; res = res->next )
      if( ( *res->name == '*' && !str_infix( res->name + 1, name ) ) || !str_cmp( res->name, name ) )
         return TRUE;
   return FALSE;
}

/*
 * Parse a name for acceptability.
 */
bool check_parse_name( char *name, bool newchar )
{
   /*
    * Names checking should really only be done on new characters, otherwise
    * we could end up with people who can't access their characters.  Would
    * have also provided for that new area havoc mentioned below, while still
    * disallowing current area mobnames.  I personally think that if we can
    * have more than one mob with the same keyword, then may as well have
    * players too though, so I don't mind that removal.  -- Alty
    */

   if( is_reserved_name( name ) && newchar )
      return FALSE;

   /*
    * Length restrictions.
    */
   if( strlen( name ) < 3 )
      return FALSE;

   if( strlen( name ) > 12 )
      return FALSE;

   /*
    * Alphanumerics only.
    * Lock out IllIll twits.
    */
   {
      char *pc;
      bool fIll;

      fIll = TRUE;
      for( pc = name; *pc != '\0'; pc++ )
      {
         if( !isalpha( *pc ) )
            return FALSE;
         if( LOWER( *pc ) != 'i' && LOWER( *pc ) != 'l' )
            fIll = FALSE;
      }

      if( fIll )
         return FALSE;
   }

   /*
    * Code that followed here used to prevent players from naming
    * themselves after mobs... this caused much havoc when new areas
    * would go in...
    */
   return TRUE;
}

/*
 * Look for link-dead player to reconnect.
 */
bool check_reconnect( DESCRIPTOR_DATA * d, char *name, bool fConn )
{
   CHAR_DATA *ch;

   for( ch = first_char; ch; ch = ch->next )
   {
      if( !IS_NPC( ch ) && ( !fConn || !ch->desc ) && ch->pcdata->filename && !str_cmp( name, ch->pcdata->filename ) )
      {
         if( fConn && ch->switched )
         {
            write_to_buffer( d, "Already playing.\r\nName: ", 0 );
            d->connected = CON_GET_NAME;
            if( d->character )
            {
               /*
                * clear descriptor pointer to get rid of bug message in log 
                */
               d->character->desc = NULL;
               free_char( d->character );
               d->character = NULL;
            }
            return BERR;
         }
         if( fConn == FALSE )
         {
            DISPOSE( d->character->pcdata->pwd );
            d->character->pcdata->pwd = str_dup( ch->pcdata->pwd );
         }
         else
         {
            /*
             * clear descriptor pointer to get rid of bug message in log 
             */
            d->character->desc = NULL;
            free_char( d->character );
            d->character = ch;
            ch->desc = d;
            ch->timer = 0;
            send_to_char( "Reconnecting.\r\n", ch );
            do_look( ch, "auto" );
            act( AT_ACTION, "$n has reconnected.", ch, NULL, NULL, TO_CANSEE );
            sprintf( log_buf, "%s@%s(%s) reconnected.", ch->pcdata->filename, d->host, d->user );
            log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
            d->connected = CON_PLAYING;
            /*
             * Inform the character of a note in progress and the possbility of continuation! 
             */
            if( ch->pcdata->in_progress )
               send_to_char( "You have a note in progress. Type \"note write\" to continue it.\r\n", ch );
         }
         return TRUE;
      }
   }
   return FALSE;
}

/*
 * Check if already playing.
 */
bool check_playing( DESCRIPTOR_DATA * d, char *name, bool kick )
{
   CHAR_DATA *ch;

   DESCRIPTOR_DATA *dold;
   int cstate;

   for( dold = first_descriptor; dold; dold = dold->next )
   {
      if( dold != d
          && ( dold->character || dold->original )
          && !str_cmp( name, dold->original ? dold->original->pcdata->filename : dold->character->pcdata->filename ) )
      {
         cstate = dold->connected;
         if( cstate < 0 )
            continue;
         ch = dold->original ? dold->original : dold->character;
         if( !ch->name || ( cstate != CON_PLAYING && cstate != CON_EDITING && cstate != CON_NOTE_TO ) )
         {
            write_to_buffer( d, "Already connected - try again.\r\n", 0 );
            sprintf( log_buf, "%s already connected.", ch->pcdata->filename );
            log_string_plus( log_buf, LOG_COMM, sysdata.log_level );
            return BERR;
         }
         if( !kick )
            return TRUE;
         write_to_buffer( d, "Already playing... Kicking off old connection.\r\n", 0 );
         write_to_buffer( dold, "Kicking off old connection... bye!\r\n", 0 );
         close_socket( dold, FALSE );
         /*
          * clear descriptor pointer to get rid of bug message in log 
          */
         d->character->desc = NULL;
         free_char( d->character );
         d->character = ch;
         ch->desc = d;
         ch->timer = 0;
         if( ch->switched )
            do_return( ch->switched, "" );
         ch->switched = NULL;
         send_to_char( "Reconnecting.\r\n", ch );
         do_look( ch, "auto" );
         act( AT_ACTION, "$n has reconnected, kicking off old link.", ch, NULL, NULL, TO_CANSEE );
         sprintf( log_buf, "%s@%s reconnected, kicking off old link.", ch->pcdata->filename, d->host );
         log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
         d->connected = cstate;
         return TRUE;
      }
   }
   return FALSE;
}

void stop_idling( CHAR_DATA * ch )
{
   ROOM_INDEX_DATA *was_in_room;
   if( !ch
       || !ch->desc
       || ch->desc->connected != CON_PLAYING || !ch->was_in_room || ch->in_room != get_room_index( ROOM_VNUM_LIMBO ) )
      return;

   ch->timer = 0;
   was_in_room = ch->was_in_room;
   char_from_room( ch );
   char_to_room( ch, was_in_room );
   act( AT_ACTION, "$n has returned from the void.", ch, NULL, NULL, TO_ROOM );
   return;
}

/*  From Erwin  */
void log_printf( const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   log_string( buf );
}

void send_to_char_color( const char *txt, CHAR_DATA * ch )
{
   DESCRIPTOR_DATA *d;
   char *colstr;
   const char *prevstr = txt;
   char colbuf[20];
   int ln;

   if( !ch )
   {
      bug( "Send_to_char_color: NULL *ch" );
      return;
   }
   if( !txt || !ch->desc )
      return;
   d = ch->desc;

   while( ( colstr = strpbrk( prevstr, "&^" ) ) != NULL )
   {
      if( colstr > prevstr )
         write_to_buffer( d, prevstr, ( colstr - prevstr ) );
      ln = make_color_sequence( colstr, colbuf, d );
      if( ln < 0 )
      {
         prevstr = colstr + 1;
         break;
      }
      else if( ln > 0 )
         write_to_buffer( d, colbuf, ln );
      prevstr = colstr + 2;
   }
   if( *prevstr )
      write_to_buffer( d, prevstr, 0 );
   return;
}

void write_to_pager( DESCRIPTOR_DATA * d, const char *txt, size_t length )
{
   int pageroffset;  /* Pager fix by thoric */

   if( length <= 0 )
      length = strlen( txt );
   if( length == 0 )
      return;
   if( !d->pagebuf )
   {
      d->pagesize = MAX_STRING_LENGTH;
      CREATE( d->pagebuf, char, d->pagesize );
   }
   if( !d->pagepoint )
   {
      d->pagepoint = d->pagebuf;
      d->pagetop = 0;
      d->pagecmd = '\0';
   }
   if( d->pagetop == 0 && !d->fcommand )
   {
      d->pagebuf[0] = '\n';
      d->pagebuf[1] = '\r';
      d->pagetop = 2;
   }
   pageroffset = d->pagepoint - d->pagebuf;  /* pager fix (goofup fixed 08/21/97) */
   while( d->pagetop + length >= d->pagesize )
   {
      if( d->pagesize > 32000 )
      {
         bug( "Pager overflow.  Ignoring.\r\n" );
         d->pagetop = 0;
         d->pagepoint = NULL;
         DISPOSE( d->pagebuf );
         d->pagesize = MAX_STRING_LENGTH;
         return;
      }
      d->pagesize *= 2;
      RECREATE( d->pagebuf, char, d->pagesize );
   }
   d->pagepoint = d->pagebuf + pageroffset;  /* pager fix (goofup fixed 08/21/97) */
   strncpy( d->pagebuf + d->pagetop, txt, length );
   d->pagetop += length;
   d->pagebuf[d->pagetop] = '\0';
   return;
}

void send_to_pager_color( const char *txt, CHAR_DATA * ch )
{
   DESCRIPTOR_DATA *d;
   char *colstr;
   const char *prevstr = txt;
   char colbuf[20];
   int ln;

   if( !ch )
   {
      bug( "Send_to_pager_color: NULL *ch" );
      return;
   }
   if( !txt || !ch->desc )
      return;
   d = ch->desc;
   ch = d->original ? d->original : d->character;
   if( IS_NPC( ch ) || !IS_SET( ch->pcdata->flags, PCFLAG_PAGERON ) )
   {
      send_to_char_color( txt, d->character );
      return;
   }

   while( ( colstr = strpbrk( prevstr, "&^" ) ) != NULL )
   {
      if( colstr > prevstr )
         write_to_pager( d, prevstr, ( colstr - prevstr ) );
      ln = make_color_sequence( colstr, colbuf, d );
      if( ln < 0 )
      {
         prevstr = colstr + 1;
         break;
      }
      else if( ln > 0 )
         write_to_pager( d, colbuf, ln );
      prevstr = colstr + 2;
   }
   if( *prevstr )
      write_to_pager( d, prevstr, 0 );
   return;
}

void set_char_color( short AType, CHAR_DATA * ch )
{
   char buf[16];
   CHAR_DATA *och;

   if( !ch || !ch->desc )
      return;

   och = ( ch->desc->original ? ch->desc->original : ch );
   if( !IS_NPC( och ) && xIS_SET( och->act, PLR_ANSI ) )
   {
      if( AType == 7 )
         strcpy( buf, "\033[m" );
      else
         sprintf( buf, "\033[0;%d;%s%dm", ( AType & 8 ) == 8, ( AType > 15 ? "5;" : "" ), ( AType & 7 ) + 30 );
      ch->desc->prevcolor = AType;  /* added this, was in 1.02 */
      ch->desc->pagecolor = AType;  /* cooridnate page & nopage shogar */
      write_to_buffer( ch->desc, buf, strlen( buf ) );
   }
   return;
}

void set_pager_color( short AType, CHAR_DATA * ch )
{
   char buf[16];
   CHAR_DATA *och;

   if( !ch || !ch->desc )
      return;

   och = ( ch->desc->original ? ch->desc->original : ch );
   if( !IS_NPC( och ) && xIS_SET( och->act, PLR_ANSI ) )
   {
      if( AType == 7 )
         strcpy( buf, "\033[m" );
      else
         sprintf( buf, "\033[0;%d;%s%dm", ( AType & 8 ) == 8, ( AType > 15 ? "5;" : "" ), ( AType & 7 ) + 30 );
      ch->desc->pagecolor = AType;
      ch->desc->prevcolor = AType;  /* cooridnate page & nopage shogar */
      send_to_pager( buf, ch );
   }
   return;
}

/* source: EOD, by John Booth <???> */
void ch_printf( CHAR_DATA * ch, const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2]; /* better safe than sorry */
   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   send_to_char( buf, ch );
}

void pager_printf( CHAR_DATA * ch, const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   send_to_pager( buf, ch );
}


/*
 * Function to strip off the "a" or "an" or "the" or "some" from an object's
 * short description for the purpose of using it in a sentence sent to
 * the owner of the object.  (Ie: an object with the short description
 * "a long dark blade" would return "long dark blade" for use in a sentence
 * like "Your long dark blade".  The object name isn't always appropriate
 * since it contains keywords that may not look proper.		-Thoric
 */
char *myobj( OBJ_DATA * obj )
{
   if( !str_prefix( "a ", obj->short_descr ) )
      return obj->short_descr + 2;
   if( !str_prefix( "an ", obj->short_descr ) )
      return obj->short_descr + 3;
   if( !str_prefix( "the ", obj->short_descr ) )
      return obj->short_descr + 4;
   if( !str_prefix( "some ", obj->short_descr ) )
      return obj->short_descr + 5;
   return obj->short_descr;
}

char *obj_short( OBJ_DATA * obj )
{
   static char buf[MAX_STRING_LENGTH];

   if( obj->count > 1 )
   {
      sprintf( buf, "%s (%d)", obj->short_descr, obj->count );
      return buf;
   }
   return obj->short_descr;
}

/*
 * The primary output interface for formatted output.
 */
/* Major overhaul. -- Alty */
void ch_printf_color( CHAR_DATA * ch, const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   send_to_char_color( buf, ch );
}

void pager_printf_color( CHAR_DATA * ch, const char *fmt, ... )
{
   char buf[MAX_STRING_LENGTH * 2];
   va_list args;

   va_start( args, fmt );
   vsprintf( buf, fmt, args );
   va_end( args );

   send_to_pager_color( buf, ch );
}

#define NAME(ch)        ( IS_NPC(ch) ? ch->short_descr : ch->name )

char *MORPHNAME( CHAR_DATA * ch )
{
   if( ch->morph && ch->morph->morph && ch->morph->morph->short_desc != NULL )
      return ch->morph->morph->short_desc;
   else
      return NAME( ch );
}

char *act_string( const char *format, CHAR_DATA * to, CHAR_DATA * ch, const void *arg1, const void *arg2, int flags )
{
   static char *const he_she[] = { "it", "he", "she" };
   static char *const him_her[] = { "it", "him", "her" };
   static char *const his_her[] = { "its", "his", "her" };
   static char buf[MAX_STRING_LENGTH];
   char fname[MAX_INPUT_LENGTH];
   char temp[MAX_STRING_LENGTH];
   char *point = buf;
   const char *str = format;
   const char *i;
   CHAR_DATA *vch = ( CHAR_DATA * ) arg2;
   OBJ_DATA *obj1 = ( OBJ_DATA * ) arg1;
   OBJ_DATA *obj2 = ( OBJ_DATA * ) arg2;

   while( str && *str != '\0' )
   {
      if( *str != '$' )
      {
         *point++ = *str++;
         continue;
      }
      ++str;
      if( !arg2 && *str >= 'A' && *str <= 'Z' )
      {
         bug( "Act: missing arg2 for code %c:", *str );
         bug( format );
         i = " <@@@@@@> ";
      }
      else
      {
         switch ( *str )
         {
            default:
               bug( "Act: bad code %c.", *str );
               i = " <@@@@@@> ";
               break;
#ifdef I3
            case '$':
               i = "$";
               break;
#endif
            case 't':
               i = ( char * )arg1;
               break;
            case 'T':
               i = ( char * )arg2;
               break;
            case 'n':
               if( ch->morph == NULL )
                  i = ( to ? PERS( ch, to ) : NAME( ch ) );
               else if( !IS_SET( flags, STRING_IMM ) )
                  i = ( to ? MORPHPERS( ch, to ) : MORPHNAME( ch ) );
               else
               {
                  sprintf( temp, "(MORPH) %s", ( to ? PERS( ch, to ) : NAME( ch ) ) );
                  i = temp;
               }
               break;
            case 'N':
               if( vch->morph == NULL )
                  i = ( to ? PERS( vch, to ) : NAME( vch ) );
               else if( !IS_SET( flags, STRING_IMM ) )
                  i = ( to ? MORPHPERS( vch, to ) : MORPHNAME( vch ) );
               else
               {
                  sprintf( temp, "(MORPH) %s", ( to ? PERS( vch, to ) : NAME( vch ) ) );
                  i = temp;
               }
               break;

            case 'e':
               if( ch->sex > 2 || ch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
                  i = "it";
               }
               else
                  i = he_she[URANGE( 0, ch->sex, 2 )];
               break;
            case 'E':
               if( vch->sex > 2 || vch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
                  i = "it";
               }
               else
                  i = he_she[URANGE( 0, vch->sex, 2 )];
               break;
            case 'm':
               if( ch->sex > 2 || ch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
                  i = "it";
               }
               else
                  i = him_her[URANGE( 0, ch->sex, 2 )];
               break;
            case 'M':
               if( vch->sex > 2 || vch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
                  i = "it";
               }
               else
                  i = him_her[URANGE( 0, vch->sex, 2 )];
               break;
            case 's':
               if( ch->sex > 2 || ch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", ch->name, ch->sex );
                  i = "its";
               }
               else
                  i = his_her[URANGE( 0, ch->sex, 2 )];
               break;
            case 'S':
               if( vch->sex > 2 || vch->sex < 0 )
               {
                  bug( "act_string: player %s has sex set at %d!", vch->name, vch->sex );
                  i = "its";
               }
               else
                  i = his_her[URANGE( 0, vch->sex, 2 )];
               break;
            case 'q':
               i = ( to == ch ) ? "" : "s";
               break;
            case 'Q':
               i = ( to == ch ) ? "your" : his_her[URANGE( 0, ch->sex, 2 )];
               break;
            case 'p':
               i = ( !to || can_see_obj( to, obj1 ) ? obj_short( obj1 ) : "something" );
               break;
            case 'P':
               i = ( !to || can_see_obj( to, obj2 ) ? obj_short( obj2 ) : "something" );
               break;
            case 'd':
               if( !arg2 || ( ( char * )arg2 )[0] == '\0' )
                  i = "door";
               else
               {
                  one_argument( ( char * )arg2, fname );
                  i = fname;
               }
               break;
         }
      }
      ++str;
      while( ( *point = *i ) != '\0' )
         ++point, ++i;

      /*
       * #0  0x80c6c62 in act_string (
       * format=0x81db42e "$n has reconnected, kicking off old link.", to=0x0, 
       * ch=0x94fcc20, arg1=0x0, arg2=0x0, flags=2) at comm.c:2901 
       */
   }
   strcpy( point, "\r\n" );
   buf[0] = UPPER( buf[0] );
   return buf;
}

#undef NAME

void act( short AType, const char *format, CHAR_DATA * ch, const void *arg1, const void *arg2, int type )
{
   char *txt;
   CHAR_DATA *to;
   CHAR_DATA *vch = ( CHAR_DATA * ) arg2;
   CHAR_DATA *third = ( CHAR_DATA * ) arg1;

   /*
    * Discard null and zero-length messages.
    */
   if( !format || format[0] == '\0' )
      return;

   if( !ch )
   {
      bug( "Act: null ch. (%s)", format );
      return;
   }

   if( !ch->in_room )
      to = NULL;
   else if( type == TO_CHAR )
      to = ch;
   else if( type == TO_THIRD )
      to = third;
   else
      to = ch->in_room->first_person;

   /*
    * ACT_SECRETIVE handling
    */
   if( IS_NPC( ch ) && xIS_SET( ch->act, ACT_SECRETIVE ) && type != TO_CHAR )
      return;

   if( type == TO_VICT )
   {
      if( !vch )
      {
         bug( "Act: null vch with TO_VICT." );
         bug( "%s (%s)", ch->name, format );
         return;
      }
      if( !vch->in_room )
      {
         bug( "Act: vch in NULL room!" );
         bug( "%s -> %s (%s)", ch->name, vch->name, format );
         return;
      }
      to = vch;
/*	to = vch->in_room->first_person;*/
   }

   if( MOBtrigger && type != TO_CHAR && type != TO_VICT && to )
   {
      OBJ_DATA *to_obj;

      txt = act_string( format, NULL, ch, arg1, arg2, STRING_IMM );
      if( HAS_PROG( to->in_room, ACT_PROG ) )
         rprog_act_trigger( txt, to->in_room, ch, ( OBJ_DATA * ) arg1, ( void * )arg2 );
      for( to_obj = to->in_room->first_content; to_obj; to_obj = to_obj->next_content )
         if( HAS_PROG( to_obj->pIndexData, ACT_PROG ) )
            oprog_act_trigger( txt, to_obj, ch, ( OBJ_DATA * ) arg1, ( void * )arg2 );
   }

   /*
    * Anyone feel like telling me the point of looping through the whole
    * room when we're only sending to one char anyways..? -- Alty 
    */
   for( ; to; to = ( type == TO_CHAR || type == TO_VICT ) ? NULL : to->next_in_room )
   {
      if( ( !to->desc && ( IS_NPC( to ) && !HAS_PROG( to->pIndexData, ACT_PROG ) ) ) || !IS_AWAKE( to ) )
         continue;

      /*
       * OasisOLC II check - Tagith 
       */
      if( to->desc && is_inolc( to->desc ) )
         continue;

      if( type == TO_CHAR && to != ch )
         continue;
      if( type == TO_THIRD && to != third )
         continue;
      if( type == TO_VICT && ( to != vch || to == ch ) )
         continue;
      if( type == TO_ROOM && to == ch )
         continue;
      if( type == TO_NOTVICT && ( to == ch || to == vch ) )
         continue;
      if( type == TO_CANSEE && ( to == ch ||
                                 ( !IS_IMMORTAL( to ) && !IS_NPC( ch ) && ( xIS_SET( ch->act, PLR_WIZINVIS )
                                                                            && ( get_trust( to ) <
                                                                                 ( ch->pcdata ? ch->pcdata->
                                                                                   wizinvis : 0 ) ) ) ) ) )
         continue;

      if( IS_IMMORTAL( to ) )
         txt = act_string( format, to, ch, arg1, arg2, STRING_IMM );
      else
         txt = act_string( format, to, ch, arg1, arg2, STRING_NONE );

      if( to->desc )
      {

         /*
          * write_to_buffer( to->desc, txt, strlen(txt) ); 
          */
         if( !IS_WRITING( to ) )
         {
            set_char_color( AType, to );
            send_to_char_color( txt, to );
         }
      }
      if( MOBtrigger )
      {
         /*
          * Note: use original string, not string with ANSI. -- Alty 
          */
         mprog_act_trigger( txt, to, ch, ( OBJ_DATA * ) arg1, ( void * )arg2 );
      }
   }
   MOBtrigger = TRUE;
   return;
}

void do_name( CHAR_DATA * ch, char *argument )
{
   char fname[1024];
   struct stat fst;
   CHAR_DATA *tmp;

   if( !NOT_AUTHED( ch ) || ch->pcdata->auth_state != 2 )
   {
      no_find( ch );
      return;
   }

   argument[0] = UPPER( argument[0] );

   if( !check_parse_name( argument, TRUE ) )
   {
      send_to_char( "Illegal name, try another.\r\n", ch );
      return;
   }

   if( !str_cmp( ch->name, argument ) )
   {
      send_to_char( "That's already your name!\r\n", ch );
      return;
   }

   for( tmp = first_char; tmp; tmp = tmp->next )
   {
      if( !str_cmp( argument, tmp->name ) )
         break;
   }

   if( tmp )
   {
      send_to_char( "That name is already taken.  Please choose another.\r\n", ch );
      return;
   }

   sprintf( fname, "%s%c/%s", PLAYER_DIR, tolower( argument[0] ), capitalize( argument ) );
   if( stat( fname, &fst ) != -1 )
   {
      send_to_char( "That name is already taken.  Please choose another.\r\n", ch );
      return;
   }

   STRFREE( ch->name );
   ch->name = STRALLOC( argument );
   STRFREE( ch->pcdata->filename );
   ch->pcdata->filename = STRALLOC( argument );
   send_to_char( "Your name has been changed.  Please apply again.\r\n", ch );
   ch->pcdata->auth_state = 0;
   return;
}

char *default_fprompt( CHAR_DATA * ch )
{
   static char buf[60];

   strcpy( buf, "&c%k&w<&Y%hhp " );
   if( IS_VAMPIRE( ch ) )
      strcat( buf, "&R%bbp " );
   else
      strcat( buf, "&C%mm " );
   strcat( buf, "&G%vmv " );
   strcat( buf, " %C&w> " );
   if( IS_NPC( ch ) || IS_IMMORTAL( ch ) )
      strcat( buf, "%i %R" );
   return buf;
}

char *default_prompt( CHAR_DATA * ch )
{
   static char buf[60];

   strcpy( buf, "&c%k&w<&Y%hhp " );
   if( IS_VAMPIRE( ch ) )
      strcat( buf, "&R%bbp" );
   else
      strcat( buf, "&C%mm" );
   strcat( buf, " &G%vmv&w> " );
   if( IS_NPC( ch ) || IS_IMMORTAL( ch ) )
      strcat( buf, "%i %R" );
   return buf;
}

int getcolor( char clr )
{
   static const char colors[16] = "xrgObpcwzRGYBPCW";
   int r;

   for( r = 0; r < 16; r++ )
      if( clr == colors[r] )
         return r;
   return -1;
}

void display_prompt( DESCRIPTOR_DATA * d )
{
   CHAR_DATA *ch = d->character;
   CHAR_DATA *och = ( d->original ? d->original : d->character );
   CHAR_DATA *victim;
   bool ansi = ( !IS_NPC( och ) && xIS_SET( och->act, PLR_ANSI ) );
   const char *prompt;
   const char *helpstart = "<Type HELP START>";
   char buf[MAX_STRING_LENGTH];
   char *pbuf = buf;
   size_t pstat, percent;

   if( !ch )
   {
      bug( "display_prompt: NULL ch" );
      return;
   }

   if( !IS_NPC( ch ) && !IS_SET( ch->pcdata->flags, PCFLAG_HELPSTART ) )
      prompt = helpstart;
   else if( !IS_NPC( ch ) && ch->substate != SUB_NONE && ch->pcdata->subprompt && ch->pcdata->subprompt[0] != '\0' )
      prompt = ch->pcdata->subprompt;
   else if( IS_NPC( ch ) || ( !ch->fighting && ( !ch->pcdata->prompt || !*ch->pcdata->prompt ) ) )
      prompt = default_prompt( ch );
   else if( ch->fighting )
   {
      if( !ch->pcdata->fprompt || !*ch->pcdata->fprompt )
         prompt = default_fprompt( ch );
      else
         prompt = ch->pcdata->fprompt;
   }
   else
      prompt = ch->pcdata->prompt;
   if( ansi )
   {
      strcpy( pbuf, "\033[m" );
      d->prevcolor = 0x07;
      pbuf += 3;
   }
   /*
    * Clear out old color stuff 
    */
   for( ; *prompt; prompt++ )
   {
      /*
       * '&' = foreground color/intensity bit
       * '^' = background color/blink bit
       * '%' = prompt commands
       * Note: foreground changes will revert background to 0 (black)
       */
      if( *prompt != '&' && *prompt != '^' && *prompt != '%' )
      {
         *( pbuf++ ) = *prompt;
         continue;
      }
      ++prompt;
      if( !*prompt )
         break;
      if( *prompt == *( prompt - 1 ) )
      {
         *( pbuf++ ) = *prompt;
         continue;
      }
      switch ( *( prompt - 1 ) )
      {
         default:
            bug( "Display_prompt: bad command char '%c'.", *( prompt - 1 ) );
            break;
         case '&':
         case '^':
            pstat = make_color_sequence( &prompt[-1], pbuf, d );
            if( pstat < 0 )
               --prompt;
            else if( pstat > 0 )
               pbuf += pstat;
            break;
         case '%':
            *pbuf = '\0';
            pstat = 0x80000000;
            switch ( *prompt )
            {
               case '%':
                  *pbuf++ = '%';
                  *pbuf = '\0';
                  break;
               case 'a':
                  if( ch->level >= 10 )
                     pstat = ch->alignment;
                  else if( IS_GOOD( ch ) )
                     strcpy( pbuf, "good" );
                  else if( IS_EVIL( ch ) )
                     strcpy( pbuf, "evil" );
                  else
                     strcpy( pbuf, "neutral" );
                  break;
               case 'A':
                  sprintf( pbuf, "%s%s%s", IS_AFFECTED( ch, AFF_INVISIBLE ) ? "I" : "",
                           IS_AFFECTED( ch, AFF_HIDE ) ? "H" : "", IS_AFFECTED( ch, AFF_SNEAK ) ? "S" : "" );
                  break;
               case 'C':  /* Tank */
                  if( ( victim = who_fighting( ch ) ) != NULL )
                  {
                     if( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;
                     if( percent >= 100 )
                        sprintf( pbuf, "Enemy[\x1b[1;31m|||\x1b[1;33m|||\x1b[1;32m||||\x1b[0m ]" );
                     else if( percent >= 90 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m|||\x1b[1;32m|||\x1b[0m  ]" );
                     else if( percent >= 80 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m|||\x1b[1;32m||\x1b[0m   ]" );
                     else if( percent >= 70 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m|||\x1b[1;32m|\x1b[0m    ]" );
                     else if( percent >= 60 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m|||\x1b[0m     ]" );
                     else if( percent >= 50 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m||\x1b[0m      ]" );
                     else if( percent >= 40 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[1;33m|\x1b[0m       ]" );
                     else if( percent >= 30 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|||\x1b[0m        ]" );
                     else if( percent >= 20 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m||\x1b[0m         ]" );
                     else if( percent >= 10 )
                        sprintf( pbuf, "Enemy[ \x1b[1;31m|\x1b[0m          ]" );
                     else
                        sprintf( pbuf, "Enemy[            ]" );
                  }
                  break;

               case 'c':

                  if( ( victim = who_fighting( ch ) ) != NULL )
                  {
                     if( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;
                     if( percent >= 60 )
                        sprintf( pbuf, "Enemy[ \x1b[1;32m%d%%\x1b[0m ]", percent );
                     else if( percent >= 30 && percent < 60 )
                        sprintf( pbuf, "Enemy[ \x1b[1;33m%d%%\x1b[0m ]", percent );
                     else
                        sprintf( pbuf, "Enemy[ \x1b[1;31m%d%%\x1b[0m ]", percent );
                  }
                  break;
               case 'h':
                  pstat = ch->hit;
                  break;
               case 'H':
                  pstat = ch->max_hit;
                  break;
               case 'm':
                  if( IS_VAMPIRE( ch ) )
                     pstat = 0;
                  else
                     pstat = ch->mana;
                  break;
               case 'M':
                  if( IS_VAMPIRE( ch ) )
                     pstat = 0;
                  else
                     pstat = ch->max_mana;
                  break;
               case 'N':  /* Tank */
                  if( !IS_IMMORTAL( ch ) )
                     break;
                  if( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
                     strcpy( pbuf, "N/A" );
                  else if( !victim->fighting || ( victim = victim->fighting->who ) == NULL )
                     strcpy( pbuf, "N/A" );
                  else
                  {
                     if( ch == victim )
                        strcpy( pbuf, "You" );
                     else if( IS_NPC( victim ) )
                        strcpy( pbuf, victim->short_descr );
                     else
                        strcpy( pbuf, victim->name );
                     pbuf[0] = UPPER( pbuf[0] );
                  }
                  break;
               case 'n':
                  if( !IS_IMMORTAL( ch ) )
                     break;
                  if( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
                     strcpy( pbuf, "N/A" );
                  else
                  {
                     if( ch == victim )
                        strcpy( pbuf, "You" );
                     else if( IS_NPC( victim ) )
                        strcpy( pbuf, victim->short_descr );
                     else
                        strcpy( pbuf, victim->name );
                     pbuf[0] = UPPER( pbuf[0] );
                  }
                  break;
               case 'T':
                  if( time_info.hour < 5 )
                     strcpy( pbuf, "night" );
                  else if( time_info.hour < 6 )
                     strcpy( pbuf, "dawn" );
                  else if( time_info.hour < 19 )
                     strcpy( pbuf, "day" );
                  else if( time_info.hour < 21 )
                     strcpy( pbuf, "dusk" );
                  else
                     strcpy( pbuf, "night" );
                  break;
               case 'b':
                  if( IS_VAMPIRE( ch ) )
                     pstat = ch->pcdata->condition[COND_BLOODTHIRST];
                  else
                     pstat = 0;
                  break;
               case 'B':
                  if( IS_VAMPIRE( ch ) )
                     pstat = ch->level + 10;
                  else
                     pstat = 0;
                  break;
               case 'u':
                  pstat = num_descriptors;
                  break;
               case 'U':
                  pstat = sysdata.maxplayers;
                  break;
               case 'v':
                  pstat = ch->move;
                  break;
               case 'V':
                  pstat = ch->max_move;
                  break;
               case 'g':
                  pstat = ch->gold;
                  break;
               case 's':
                  pstat = ch->silver;
                  break;
               case 'd':
                  pstat = ch->copper;
                  break;
               case 'r':
                  if( IS_IMMORTAL( och ) )
                     pstat = ch->in_room->vnum;
                  break;
               case 'F':
                  if( IS_IMMORTAL( och ) )
                     sprintf( pbuf, "%s", flag_string( ch->in_room->room_flags, r_flags ) );
                  break;
               case 'R':
                  if( xIS_SET( och->act, PLR_ROOMVNUM ) )
                     sprintf( pbuf, "<#%d> ", ch->in_room->vnum );
                  break;
               case 'k':
                  if( xIS_SET( ch->act, PLR_AFK ) )
                     /*
                      * sprintf(pbuf, "&B<&RAFK&B> "); NO COLOR! 
                      */
                     sprintf( pbuf, "<AFK> " );
                  break;
               case 'x':
                  pstat = ch->exp;
                  break;
               case 'X':
                  pstat = exp_level( ch, ch->level + 1 ) - ch->exp;
                  break;
               case 'S':
                  if( ch->style == STYLE_BERSERK )
                     strcpy( pbuf, "B" );
                  else if( ch->style == STYLE_AGGRESSIVE )
                     strcpy( pbuf, "A" );
                  else if( ch->style == STYLE_DEFENSIVE )
                     strcpy( pbuf, "D" );
                  else if( ch->style == STYLE_EVASIVE )
                     strcpy( pbuf, "E" );
                  else
                     strcpy( pbuf, "S" );
                  break;
               case 'i':
                  if( ( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_WIZINVIS ) ) ||
                      ( IS_NPC( ch ) && xIS_SET( ch->act, ACT_MOBINVIS ) ) )
                     sprintf( pbuf, "(Invis %d) ", ( IS_NPC( ch ) ? ch->mobinvis : ch->pcdata->wizinvis ) );
                  else if( IS_AFFECTED( ch, AFF_INVISIBLE ) )
                     sprintf( pbuf, "(Invis) " );
                  break;
               case 'I':
                  pstat = ( IS_NPC( ch ) ? ( xIS_SET( ch->act, ACT_MOBINVIS ) ? ch->mobinvis : 0 )
                           : ( xIS_SET( ch->act, PLR_WIZINVIS ) ? ch->pcdata->wizinvis : 0 ) );
                  break;
            }
            if( pstat != 0x80000000 )
               sprintf( pbuf, "%d", pstat );
            pbuf += strlen( pbuf );
            break;
      }
   }
   *pbuf = '\0';
   write_to_buffer( d, buf, ( pbuf - buf ) );
   return;
}

int make_color_sequence( const char *col, char *buf, DESCRIPTOR_DATA * d )
{
   int ln;
   const char *ctype = col;
   unsigned char cl;
   CHAR_DATA *och;
   bool ansi;

   och = ( d->original ? d->original : d->character );
   ansi = ( !IS_NPC( och ) && xIS_SET( och->act, PLR_ANSI ) );
   col++;
   if( !*col )
      ln = -1;
   else if( *ctype != '&' && *ctype != '^' )
   {
      bug( "Make_color_sequence: command '%c' not '&' or '^'.", *ctype );
      ln = -1;
   }
   else if( *col == *ctype )
   {
      buf[0] = *col;
      buf[1] = '\0';
      ln = 1;
   }
   else if( !ansi )
      ln = 0;
   else
   {
      cl = d->prevcolor;
      switch ( *ctype )
      {
         default:
            bug( "Make_color_sequence: bad command char '%c'.", *ctype );
            ln = -1;
            break;
         case '&':
            if( *col == '-' )
            {
               buf[0] = '~';
               buf[1] = '\0';
               ln = 1;
               break;
            }
         case '^':
         {
            int newcol;

            if( ( newcol = getcolor( *col ) ) < 0 )
            {
               ln = 0;
               break;
            }
            else if( *ctype == '&' )
               cl = ( cl & 0xF0 ) | newcol;
            else
               cl = ( cl & 0x0F ) | ( newcol << 4 );
         }
            if( cl == d->prevcolor )
            {
               ln = 0;
               break;
            }
            strcpy( buf, "\033[" );
            if( ( cl & 0x88 ) != ( d->prevcolor & 0x88 ) )
            {
               strcat( buf, "m\033[" );
               if( ( cl & 0x08 ) )
                  strcat( buf, "1;" );
               if( ( cl & 0x80 ) )
                  strcat( buf, "5;" );
               d->prevcolor = 0x07 | ( cl & 0x88 );
               ln = strlen( buf );
            }
            else
               ln = 2;
            if( ( cl & 0x07 ) != ( d->prevcolor & 0x07 ) )
            {
               sprintf( buf + ln, "3%d;", cl & 0x07 );
               ln += 3;
            }
            if( ( cl & 0x70 ) != ( d->prevcolor & 0x70 ) )
            {
               sprintf( buf + ln, "4%d;", ( cl & 0x70 ) >> 4 );
               ln += 3;
            }
            if( buf[ln - 1] == ';' )
               buf[ln - 1] = 'm';
            else
            {
               buf[ln++] = 'm';
               buf[ln] = '\0';
            }
            d->prevcolor = cl;
      }
   }
   if( ln <= 0 )
      *buf = '\0';
   return ln;
}

void set_pager_input( DESCRIPTOR_DATA * d, char *argument )
{
   while( isspace( *argument ) )
      argument++;
   d->pagecmd = *argument;
   return;
}

bool pager_output( DESCRIPTOR_DATA * d )
{
   register char *last;
   CHAR_DATA *ch;
   int pclines;
   register int lines;
   bool ret;

   if( !d || !d->pagepoint || d->pagecmd == -1 )
      return TRUE;
   ch = d->original ? d->original : d->character;
   pclines = UMAX( ch->pcdata->pagerlen, 5 ) - 1;
   switch ( LOWER( d->pagecmd ) )
   {
      default:
         lines = 0;
         break;
      case 'b':
         lines = -1 - ( pclines * 2 );
         break;
      case 'r':
         lines = -1 - pclines;
         break;
      case 'q':
         d->pagetop = 0;
         d->pagepoint = NULL;
         flush_buffer( d, TRUE );
         DISPOSE( d->pagebuf );
         d->pagesize = MAX_STRING_LENGTH;
         return TRUE;
   }
   while( lines < 0 && d->pagepoint >= d->pagebuf )
      if( *( --d->pagepoint ) == '\n' )
         ++lines;
   if( *d->pagepoint == '\n' && *( ++d->pagepoint ) == '\r' )
      ++d->pagepoint;
   if( d->pagepoint < d->pagebuf )
      d->pagepoint = d->pagebuf;
   for( lines = 0, last = d->pagepoint; lines < pclines; ++last )
      if( !*last )
         break;
      else if( *last == '\n' )
         ++lines;
   if( *last == '\r' )
      ++last;
   if( last != d->pagepoint )
   {
      if( !write_to_descriptor( d, d->pagepoint, ( last - d->pagepoint ) ) )
         return FALSE;
      d->pagepoint = last;
   }
   while( isspace( *last ) )
      ++last;
   if( !*last )
   {
      d->pagetop = 0;
      d->pagepoint = NULL;
      flush_buffer( d, TRUE );
      DISPOSE( d->pagebuf );
      d->pagesize = MAX_STRING_LENGTH;
      return TRUE;
   }
   d->pagecmd = -1;
   if( xIS_SET( ch->act, PLR_ANSI ) )
      if( write_to_descriptor( d, "\033[1;36m", 7 ) == FALSE )
         return FALSE;
   if( ( ret = write_to_descriptor( d, "(C)ontinue, (R)efresh, (B)ack, (Q)uit: [C] ", 0 ) ) == FALSE )
      return FALSE;
   if( xIS_SET( ch->act, PLR_ANSI ) )
   {
      char buf[32];

      if( d->pagecolor == 7 )
         strcpy( buf, "\033[m" );
      else
         sprintf( buf, "\033[0;%d;%s%dm", ( d->pagecolor & 8 ) == 8,
                  ( d->pagecolor > 15 ? "5;" : "" ), ( d->pagecolor & 7 ) + 30 );
      ret = write_to_descriptor( d, buf, 0 );
   }
   return ret;
}

/*  Warm reboot stuff, gotta make sure to thank Erwin for this :) */
void do_copyover( CHAR_DATA * ch, char *argument )
{
   FILE *fp;
   DESCRIPTOR_DATA *d;
   char buf[100], buf2[100], buf3[100], buf4[100];

   fp = fopen( COPYOVER_FILE, "w" );

   if( !fp )
   {
      send_to_char( "Copyover file not writeable, aborted.\r\n", ch );
      log_printf( "Could not write to copyover file: %s", COPYOVER_FILE );
      perror( "do_copyover:fopen" );
      return;
   }

#ifdef WEBSVR
   if( sysdata.webtoggle == TRUE )
   {
      shutdown_web(  );
   }
#endif

   /*
    * Consider changing all saved areas here, if you use OLC 
    */

#ifdef ISPELL
   /*
    * Shutdown ispell 
    */
   ispell_done(  );
#endif

   sprintf( buf, "\r\n[INFO] COPYOVER by %s - please remain seated!\r\n\a", ch->name );
   /*
    * For each playing descriptor, save its state 
    */
   for( d = first_descriptor; d; d = d_next )
   {
      CHAR_DATA *och = CH( d );
      d_next = d->next; /* We delete from the list , so need to save this */

#ifdef MCCP
      // End Compression
      if( d && d->out_compress )
         compressEnd( d );
#endif

      if( !d->character || d->connected < 0 )   /* drop those logging on */
      {
         write_to_descriptor( d, "\r\nSorry, we are rebooting." " Come back in a few minutes.\r\n", 0 );
         close_socket( d, FALSE );  /* throw'em out */
      }
      else
      {
         fprintf( fp, "%d %s %s\n", d->descriptor, och->name, d->host );
         if( och->level == 1 )
         {
            write_to_descriptor( d, "Since you are level one,"
                                 "and level one characters do not save, you gain a free level!\r\n", 0 );
            advance_level( och, TRUE );
            och->level++;  /* Advance_level doesn't do that */
         }
         save_char_obj( och );
         write_to_descriptor( d, buf, 0 );
      }
   }
#ifdef I3
   if( I3_is_connected(  ) )
   {
      I3_savechanlist(  );
      I3_savemudlist(  );
   }
#endif
   fprintf( fp, "-1\n" );
   fclose( fp );
   fp = NULL;

#ifdef IMC
   imc_hotboot(  );
#endif

   /*
    * exec - descriptors are inherited 
    */
   sprintf( buf, "%d", port );
   sprintf( buf2, "%d", control );
#ifdef I3
   sprintf( buf3, "%d", I3_socket );
#else
   strcpy( buf3, "-1" );
#endif
#ifdef IMC
   if( this_imcmud )
      snprintf( buf4, 100, "%d", this_imcmud->desc );
   else
      strncpy( buf4, "-1", 100 );
#else
   strncpy( buf4, "-1", 100 );
#endif
   save_world( NULL );

   execl( EXE_FILE, "rmexe", buf, "copyover", buf2, buf3, buf4, ( char * )NULL );

   /*
    * Failed - sucessful exec will not return 
    */

   perror( "do_copyover: execl" );
   send_to_char( "Copyover FAILED!\r\n", ch );
}

/* Recover from a copyover - load players */
void copyover_recover(  )
{
   DESCRIPTOR_DATA *d;
   FILE *fp;
   char name[100];
   char host[MAX_STRING_LENGTH];
   int desc;
   bool fOld;

   log_string( "Copyover recovery initiated" );

   fp = fopen( COPYOVER_FILE, "r" );

   if( !fp )   /* there are some descriptors open which will hang forever then ? */
   {
      perror( "copyover_recover:fopen" );
      log_string( "Copyover file not found. Exitting.\r\n" );
      exit( 1 );
   }

   unlink( COPYOVER_FILE );   /* In case something crashes
                               * - doesn't prevent reading */
   for( ;; )
   {
      fscanf( fp, "%d %s %s\n", &desc, name, host );
      if( desc == -1 )
         break;

#ifdef MCCP
      /*
       * Write something, and check if it goes error-free 
       */
      if( !write_to_descriptor2( desc, ( char * )compress_on_str, 0 ) )
      {
         close( desc ); /* nope */
         continue;
      }
#else
      if( !write_to_descriptor2( desc, "\r\nRestoring from copyover...\r\n", 0 ) )
      {
         close( desc );
         continue;
      }
#endif
      CREATE( d, DESCRIPTOR_DATA, 1 );
      init_descriptor( d, desc );   /* set up various stuff */
#ifdef MCCP
      write_to_descriptor( d, "\r\nRestoring from copyover...\r\n", 0 );
#endif
      d->host = STRALLOC( host );

      LINK( d, first_descriptor, last_descriptor, next, prev );
      d->connected = CON_COPYOVER_RECOVER;   /* negative so close_socket
                                              * will cut them off */

      /*
       * Now, find the pfile 
       */

      fOld = load_char_obj( d, name, FALSE );

      if( !fOld ) /* Player file not found?! */
      {
         write_to_descriptor( d, "\r\nSomehow, your character was lost in the copyover sorry.\r\n", 0 );
         close_socket( d, FALSE );
      }
      else  /* ok! */
      {
         write_to_descriptor( d, "\r\nCopyover recovery complete.\r\n", 0 );
         /*
          * no longer needed - but save for rainy day, shogar 
          */
         /*
          * write_to_descriptor (desc, "\r\nLoading mass quantities of areas...\r\nPerhaps a good time to go wash the car or rotate the tires\r\n",0);
          */
         /*
          * Just In Case,  Someone said this isn't necassary, but _why_
          * do we want to dump someone in limbo? 
          */
         if( !d->character->in_room )
            d->character->in_room = get_room_index( ROOM_VNUM_TEMPLE );

         /*
          * Insert in the char_list 
          */
         LINK( d->character, first_char, last_char, next, prev );

         d->character->pcdata->board = &boards[0];
         char_to_room( d->character, d->character->in_room );
         do_look( d->character, "auto noprog" );
         act( AT_ACTION, "$n materializes!", d->character, NULL, NULL, TO_ROOM );
         d->connected = CON_PLAYING;
         num_descriptors++;
      }

   }
   fclose( fp );
}

/* Color align functions by Justice@Aaern */
const char *const_color_align( const char *argument, int size, int align )
{
   int space = ( size - const_color_str_len( argument ) );
   static char buf[MAX_STRING_LENGTH];

   if( align == ALIGN_RIGHT || const_color_str_len( argument ) >= size )
      sprintf( buf, "%*.*s", const_color_strnlen( argument, size ), const_color_strnlen( argument, size ), argument );
   else if( align == ALIGN_CENTER )
      sprintf( buf, "%*s%s%*s", ( space / 2 ), "", argument,
               ( ( space / 2 ) * 2 ) == space ? ( space / 2 ) : ( ( space / 2 ) + 1 ), "" );
   else
      sprintf( buf, "%s%*s", argument, space, "" );

   return buf;
}

char *color_align( char *argument, int size, int align )
{
   int space = ( size - color_str_len( argument ) );
   static char buf[MAX_STRING_LENGTH];

   if( align == ALIGN_RIGHT || color_str_len( argument ) >= size )
      sprintf( buf, "%*.*s", color_strnlen( argument, size ), color_strnlen( argument, size ), argument );
   else if( align == ALIGN_CENTER )
      sprintf( buf, "%*s%s%*s", ( space / 2 ), "", argument,
               ( ( space / 2 ) * 2 ) == space ? ( space / 2 ) : ( ( space / 2 ) + 1 ), "" );
   else if( align == ALIGN_LEFT )
      sprintf( buf, "%s%*s", argument, space, "" );

   return buf;
}

int const_color_str_len( const char *argument )
{
   int str, count = 0;
   bool IS_COLOR = FALSE;

   for( str = 0; argument[str] != '\0'; str++ )
   {
      if( argument[str] == '&' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else if( argument[str] == '^' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else
      {
         if( IS_COLOR == FALSE )
            count++;
         else
            IS_COLOR = FALSE;
      }
   }

   return count;
}

int const_color_strnlen( const char *argument, int maxlength )
{
   int str, count = 0;
   bool IS_COLOR = FALSE;

   for( str = 0; argument[str] != '\0'; str++ )
   {
      if( argument[str] == '&' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else if( argument[str] == '^' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else
      {
         if( IS_COLOR == FALSE )
            count++;
         else
            IS_COLOR = FALSE;
      }

      if( count >= maxlength )
         break;
   }
   if( count < maxlength )
      return ( ( str - count ) + maxlength );

   str++;
   return str;
}

int color_str_len( char *argument )
{
   int str, count = 0;
   bool IS_COLOR = FALSE;

   for( str = 0; argument[str] != '\0'; str++ )
   {
      if( argument[str] == '&' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else if( argument[str] == '^' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else
      {
         if( IS_COLOR == FALSE )
            count++;
         else
            IS_COLOR = FALSE;
      }
   }

   return count;
}

int color_strnlen( char *argument, int maxlength )
{
   int str, count = 0;
   bool IS_COLOR = FALSE;

   for( str = 0; argument[str] != '\0'; str++ )
   {
      if( argument[str] == '&' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else if( argument[str] == '^' )
      {
         if( IS_COLOR == TRUE )
         {
            count++;
            IS_COLOR = FALSE;
         }
         else
            IS_COLOR = TRUE;
      }
      else
      {
         if( IS_COLOR == FALSE )
            count++;
         else
            IS_COLOR = FALSE;
      }

      if( count >= maxlength )
         break;
   }
   if( count < maxlength )
      return ( ( str - count ) + maxlength );

   str++;
   return str;
}

#ifdef WIN32

void shutdown_mud( char *reason );

void bailout( void )
{
   echo_to_all( AT_IMMORT, "MUD shutting down by system operator NOW!!", ECHOTAR_ALL );
   shutdown_mud( "MUD shutdown by system operator" );
   log_string( "MUD shutdown by system operator" );
   Sleep( 5000 ); /* give "echo_to_all" time to display */
   mud_down = TRUE;  /* This will cause game_loop to exit */
   service_shut_down = TRUE;  /* This will cause characters to be saved */
   fflush( stderr );
   return;
}

#endif