eldhamud_2.2.5/clans/
eldhamud_2.2.5/classes/
eldhamud_2.2.5/councils/
eldhamud_2.2.5/deity/
eldhamud_2.2.5/doc/
eldhamud_2.2.5/doc/DIKU/
eldhamud_2.2.5/doc/DIKU/.svn/
eldhamud_2.2.5/doc/DIKU/.svn/prop-base/
eldhamud_2.2.5/doc/DIKU/.svn/text-base/
eldhamud_2.2.5/doc/MERC/
eldhamud_2.2.5/doc/MERC/.svn/
eldhamud_2.2.5/doc/SMAUG/.svn/
eldhamud_2.2.5/doc/SMAUG/mudprogs/
eldhamud_2.2.5/doc/SMAUG/mudprogs/.svn/
eldhamud_2.2.5/gods/
eldhamud_2.2.5/houses/
eldhamud_2.2.5/player/
eldhamud_2.2.5/player/a/
eldhamud_2.2.5/races/
eldhamud_2.2.5/src/Headers/.svn/
eldhamud_2.2.5/src/ObjectFiles/.svn/
/****************************************************************************
 *			Eldhamud Codebase V2.2				    *
 * ------------------------------------------------------------------------ *
 *          EldhaMUD code (C) 2003-2008 by Robert Powell (Tommi)            *
 * ------------------------------------------------------------------------ *
 * Original SMAUG 1.4a written by Thoric (Derek Snider) with Altrag,        *
 * Blodkai, Haus, Narn, Scryn, Swordbearer, Tricops, Gorog, Rennard,        *
 * Grishnakh, Fireblade, and Nivek.                                         *
 *                                                                          *
 * Original MERC 2.1 code by Hatchet, Furey, and Kahn.                      *
 *                                                                          *
 * Original DikuMUD code by: Hans Staerfeldt, Katja Nyboe, Tom Madsen,      *
 * Michael Seifert, and Sebastian Hammer.                                   *
 * ------------------------------------------------------------------------ *
 *                      Low-level communication module                      *
 ****************************************************************************/
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include "./Headers/mud.h"
#include "./Headers/md5.h"
/*
 * Socket and TCP/IP stuff.
 */
#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>
#define closesocket close
#ifdef sun
int gethostname( char *name, int namelen );
#endif
const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, STRING_NULL };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, STRING_NULL };
const char go_ahead_str[] = { IAC, GA, STRING_NULL };
void auth_maxdesc args( ( int *md, fd_set * ins, fd_set * outs, fd_set * excs ) );
void auth_check args( ( fd_set * ins, fd_set * outs, fd_set * excs ) );
void set_auth args( ( DESCRIPTOR_DATA * d ) );
void kill_auth args( ( DESCRIPTOR_DATA * d ) );
void save_sysdata args( ( SYSTEM_DATA sys ) );
/*
 * Global variables.
 */
DESCRIPTOR_DATA *first_descriptor;  /* First descriptor     */
DESCRIPTOR_DATA *last_descriptor;   /* Last descriptor      */
DESCRIPTOR_DATA *d_next;   /* Next descriptor in loop */
int num_descriptors;
FILE *fpReserve;  /* Reserved file handle    */
bool mud_down; /* Shutdown       */
bool service_shut_down; /* Shutdown by operator closing down service */
bool wizlock;  /* Game is wizlocked    */
time_t boot_time;
char str_boot_time[MAX_INPUT_LENGTH];
char lastplayercmd[MAX_INPUT_LENGTH * 2];
time_t current_time; /* Time of this pulse      */
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)";
short client_speed( short speed );
/*
 * OS-dependent local functions.
 */
void game_loop args( ( void ) );
int init_socket args( ( int mudport ) );
void new_descriptor args( ( int new_desc ) );
bool read_from_descriptor args( ( DESCRIPTOR_DATA * d ) );
bool write_to_descriptor args( ( int desc, char *txt, int length ) );
/*
 * Other local functions (OS-independent).
 */
bool check_reconnect args( ( DESCRIPTOR_DATA * d, char *name, bool fConn ) );
bool check_playing args( ( DESCRIPTOR_DATA * d, char *name, bool kick ) );
int main args( ( int argc, char **argv ) );
void nanny args( ( DESCRIPTOR_DATA * d, char *argument ) );
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 ) );
void set_pager_input args( ( DESCRIPTOR_DATA * d, char *argument ) );
bool pager_output args( ( DESCRIPTOR_DATA * d ) );
void mail_count args( ( CHAR_DATA * ch ) );
int port;
int main( int argc, char **argv )
{

   struct timeval now_time;
   char hostn[128];
   bool fCopyOver = FALSE;
   /*
    * Memory debugging if needed.
    */
#if defined(MALLOC_DEBUG)
   malloc_debug( 2 );
#endif
   DONT_UPPER = FALSE;
   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;
   boot_time = time( 0 );  /*  <-- I think this is what you wanted */
   strcpy( str_boot_time, ctime( &current_time ) );
   init_pfile_scan_time(  );  /* Pfile autocleanup initializer - Samson 5-8-99 */
   /*
    * Reserve two channels for our use.
    */

   if( ( fpReserve = fopen( NULL_FILE, "r" ) ) == NULL )
   {
      perror( NULL_FILE );
      exit( 1 );
   }

   if( ( fpLOG = fopen( NULL_FILE, "r" ) ) == NULL )
   {
      perror( NULL_FILE );
      exit( 1 );
   }

   /*
    * Get the port number.
    */
   port = 8000;

   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] );
      }
      else
         fCopyOver = FALSE;
   }

   /*
    * Run the game.
    */
   log_string( "Booting Database" );

   boot_db( fCopyOver );

   log_string( "Initializing socket" );

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

   /*
    * 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 );

   if( fCopyOver )
   {
      log_string( "Initiating hotboot recovery." );
      hotboot_recover(  );
   }

   game_loop(  );

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

int init_socket( int mudport )
{
   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" );
      closesocket( 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" );
         closesocket( fd );
         exit( 1 );
      }
   }

#endif
   memset( &sa, STRING_NULL, sizeof( sa ) );

   sa.sin_family = AF_INET;

   sa.sin_port = htons( mudport );

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

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

   return fd;
}

static void SegVio(  )
{
   abort(  );
}

/*
 * LAG alarm!       -Thoric
 */
void caught_alarm( int signum )
{
   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!\n\r" );
   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
    */
#if defined(MALLOC_DEBUG)

   if( malloc_verify(  ) != 1 )
      abort(  );

#endif
   /*
    * 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( void )
{

   struct timeval last_time;
   char cmdline[MAX_INPUT_LENGTH];
   DESCRIPTOR_DATA *d;
   /*
    * time_t last_check = 0;
    */
   signal( SIGSEGV, SegVio );
   gettimeofday( &last_time, NULL );
   current_time = ( time_t ) last_time.tv_sec;
   /*
    * Main loop
    */

   while( !mud_down )
   {
      accept_new( control );
      /*
       * 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 ) )
               save_char_obj( d->character );

            d->outtop = 0;

            close_socket( d, TRUE );

            continue;
         }
         else if( ( ( !d->character && d->idle > 1200 )  /* 5 mins */
                    || ( d->connected != CON_PLAYING && d->idle > 2400 )   /* 10 mins */
                    || d->idle > 57600 )  /* 4 hrs */
                  && !( d->character && d->character->level >= LEVEL_IMMORTAL ) )
         {
            write_to_descriptor( d->descriptor, "Idle timeout... disconnecting.\n\r", 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 ) )
                     save_char_obj( d->character );

                  d->outtop = 0;

                  close_socket( d, FALSE );

                  continue;
               }
            }

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

            read_from_buffer( d );

            if( d->incomm[0] != STRING_NULL )
            {
               d->fcommand = TRUE;
               stop_idling( d->character );
               strcpy( cmdline, d->incomm );
               d->incomm[0] = STRING_NULL;

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

               if( d->pagepoint )
                  set_pager_input( d, cmdline );
               else
                  switch ( d->connected )
                  {

                     default:
                        nanny( d, cmdline );
                        break;

                     case CON_PLAYING:
                        d->character->cmd_recurse = 0;
                        interpret( d->character, cmdline );
                        break;

                     case CON_EDITING:
                        edit_buffer( d->character, cmdline );
                        break;
                  }
            }
         }

         if( d == last_descriptor )
            break;
      }

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

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

         if( ( d->fcommand || d->outtop > 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 ) )
                     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 ) )
                  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;

            if( select( 0, NULL, NULL, NULL, &stall_time ) < 0 && errno != EINTR )
            {
               perror( "game_loop: select: stall" );
               exit( 1 );
            }
         }
      }

      gettimeofday( &last_time, NULL );

      current_time = ( time_t ) last_time.tv_sec;
   }

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

   return;
}

void new_descriptor( int new_desc )
{
   char buf[MAX_STRING_LENGTH];
   DESCRIPTOR_DATA *dnew;

   struct sockaddr_in sock;

   struct hostent *from;
   size_t desc, size;
   char bugbuf[MAX_STRING_LENGTH];
   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";

   if( fcntl( desc, F_SETFL, FNDELAY ) == -1 )
   {
      perror( "New_descriptor: fcntl: FNDELAY" );
      set_alarm( 0 );
      return;
   }

   if( check_bad_desc( new_desc ) )
      return;

   CREATE( dnew, DESCRIPTOR_DATA, 1 );

   dnew->next = NULL;

   dnew->descriptor = desc;

   dnew->connected = CON_GET_NAME;

   dnew->outsize = 2000;

   dnew->idle = 0;

   dnew->lines = 0;

   dnew->scrlen = 24;

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

   dnew->newstate = 0;

   dnew->prevcolor = 0x07;

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

   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( desc, "Your site has been banned from this Mud.\n\r", 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 );

   /*
    * Send the greeting.
    */
   {
      extern char *help_greeting;

      if( help_greeting[0] == '.' )
         write_to_buffer( dnew, help_greeting + 1, 0 );
      else
         write_to_buffer( dnew, help_greeting, 0 );
   }

   if( ++num_descriptors > sysdata.maxplayers )
      sysdata.maxplayers = 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, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );

      save_sysdata( sysdata );
   }

   set_alarm( 0 );

   return;
}

void free_desc( DESCRIPTOR_DATA * d )
{
   closesocket( d->descriptor );
   STRFREE( d->host );
   DISPOSE( d->outbuf );

   if( d->pagebuf )
      DISPOSE( d->pagebuf );

   DISPOSE( d );

   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.\n\r", 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 )
   {
      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 ) )
      {
         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 );
   }

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

   free_desc( dclose );

   --num_descriptors;

   return;
}

bool read_from_descriptor( DESCRIPTOR_DATA * d )
{
   int iStart, iErr;
   /*
    * Hold horses if pending command already.
    */

   if( d->incomm[0] != STRING_NULL )
      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->descriptor, "\n\r*** PUT A LID ON IT!!! ***\n\r" "You cannot enter the same command more than 20 consecutive times!\n\r", 0 );
      return FALSE;
   }

   for( ;; )
   {
      int nRead;
      nRead = recv( d->descriptor, d->inbuf + iStart, sizeof( d->inbuf ) - 10 - iStart, 0 );
      iErr = errno;

      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] = STRING_NULL;

   return TRUE;
}

/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer( DESCRIPTOR_DATA * d )
{
   int i, j, k;
   /*
    * Hold horses if pending command already.
    */

   if( d->incomm[0] != STRING_NULL )
      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] == STRING_NULL )
         return;
   }

   /*
    * Canonical input processing.
    */
   for( i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++ )
   {
      if( k >= 508 )
      {
         write_to_descriptor( d->descriptor, "Line too long.\n\r", 0 );
         /*
          * skip the rest of the line
          */
         /*
          * for ( ; d->inbuf[i] != STRING_NULL || i>= MAX_INBUF_SIZE ; i++ )
          * {
          * if ( d->inbuf[i] == '\n' || d->inbuf[i] == '\r' )
          * break;
          * }
          */
         d->inbuf[i] = '\n';
         d->inbuf[i + 1] = STRING_NULL;
         break;
      }

      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] = STRING_NULL;

   /*
    * Deal with bozos with #repeat 1000 ...
    */
   if( k > 1 || d->incomm[0] == '!' )
   {
      if( d->incomm[0] != '!' && strcmp( d->incomm, d->inlast ) )
      {
         d->repeat = 0;
      }
   }

   /*
    * Do '!' substitution.
    */
   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] ) != STRING_NULL; j++ )
      ;

   return;
}

/*
 * Low level output function.
 */
bool flush_buffer( DESCRIPTOR_DATA * d, bool fPrompt )
{
   char buf[MAX_INPUT_LENGTH * UMAX( 1, d->speed )];
   /*
    * If buffer has more than 4K inside, spit out .5K at a time   -Thoric
    */

   if( !mud_down && d->outtop > client_speed( d->speed ) )
   {
      memcpy( buf, d->outbuf, client_speed( d->speed ) );
      d->outtop -= client_speed( d->speed );
      memmove( d->outbuf, d->outbuf + client_speed( d->speed ), d->outtop );

      if( d->snoop_by )
      {
         char snoopbuf[MAX_INPUT_LENGTH * UMAX( 1, d->speed )];
         buf[client_speed( d->speed )] = STRING_NULL;

         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->descriptor, buf, client_speed( d->speed ) ) )
      {
         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, "\n\r", 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->descriptor, 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, int 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 );

   /*
    * Uncomment if debugging or something
    * if ( length != strlen(txt) )
    * {
    * bug( "Write_to_buffer: length(%d) != strlen(txt)!", length );
    * length = strlen(txt);
    * }
    */
   /*
    * Initial \n\r if needed.
    */
   if( d->outtop == 0 && !d->fcommand )
   {
      d->outbuf[0] = '\n';
      d->outbuf[1] = '\r';
      d->outtop = 2;
   }

   /*
    * Expand the buffer as needed.
    */
   while( d->outtop + length >= d->outsize )
   {
      if( d->outsize > 32000 )
      {
         /*
          * empty buffer
          */
         d->outtop = 0;
         /*
          * Bugfix by Samson - moved bug() call up
          */
         bug( "Buffer overflow. Closing (%s).", d->character ? d->character->name : "???" );
         close_socket( d, TRUE );
         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] = STRING_NULL;

   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.
 *
 * Added block checking to prevent random booting of the descriptor. Thanks go
 * out to Rustry for his suggestions. -Orion
 */
bool write_to_descriptor( int desc, char *txt, int length )
{
   int iStart = 0;
   int nWrite = 0;
   int nBlock = 0;
   int iErr = 0;

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

   for( iStart = 0; iStart < length; iStart += nWrite )
   {
      nBlock = UMIN( length - iStart, 4096 );
      nWrite = send( desc, txt + iStart, nBlock, 0 );

      if( nWrite == -1 )
      {
         iErr = errno;

         if( iErr == EWOULDBLOCK )
         {
            /*
             * This is a SPAMMY little bug error. I would suggest
             * not using it, but I've included it in case. -Orion
             *
             perror( "Write_to_descriptor: Send is blocking" );
             */
            nWrite = 0;
            continue;
         }
         else
         {
            perror( "Write_to_descriptor" );
            return FALSE;
         }
      }
   }

   return TRUE;
}

char *smaug_crypt( const char *pwd )
{
   md5_state_t state;
   md5_byte_t digest[16];
   static char passwd[16];
   unsigned int x;
   md5_init( &state );
   md5_append( &state, ( const md5_byte_t * )pwd, strlen( pwd ) );
   md5_finish( &state, digest );
   strncpy( passwd, ( const char * )digest, 16 );
   /*
    * The listed exceptions below will fubar the MD5 authentication packets, so change them
    */

   for( x = 0; x < strlen( passwd ); x++ )
   {
      if( passwd[x] == '\n' )
         passwd[x] = 'n';

      if( passwd[x] == '\r' )
         passwd[x] = 'r';

      if( passwd[x] == '\t' )
         passwd[x] = 't';

      if( passwd[x] == ' ' )
         passwd[x] = 's';

      if( ( int )passwd[x] == 11 )
         passwd[x] = 'x';

      if( ( int )passwd[x] == 12 )
         passwd[x] = 'X';

      if( passwd[x] == '~' )
         passwd[x] = '+';

      if( passwd[x] == EOF )
         passwd[x] = 'E';
   }

   return ( passwd );
}

/*
 * Deal with sockets that haven't logged in yet.
 */
void nanny( DESCRIPTOR_DATA * d, char *argument )
{
   /*
    * extern int lang_array[];
    * extern char *lang_names[];
    */
   char buf[MAX_STRING_LENGTH];
   char arg[MAX_STRING_LENGTH];
   CHAR_DATA *ch;
   char *pwdnew;
   char *p;
   int iRace, Class;
   bool fOld, chk;

   if( d->connected != CON_NOTE_TEXT )
   {
      while( isspace( *argument ) )
         argument++;
   }

   ch = d->character;

   switch ( d->connected )
   {

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

      case CON_GET_NAME:

         if( argument[0] == STRING_NULL )
         {
            close_socket( d, FALSE );
            return;
         }

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

         if( !check_parse_name( argument, ( d->newstate != 0 ) ) )
         {
            sprintf( buf, "Sorry, %s does not pass the profanity filter, Please enter another name: ", argument );
            write_to_buffer( d, buf, 0 );
            return;
         }

         if( !str_cmp( argument, "Register" ) )
         {
            if( d->newstate == 0 )
            {
               if( sysdata.DENY_NEW_PLAYERS == TRUE )
               {
                  send_to_desc_color( "\033[1;35mPlease be patient, the game server is currently undergoing routine system \n\r", d );
                  send_to_desc_color( "\033[1;35mmaintainence. New players are not accepted at this time. Please try again \n\r", d );
                  send_to_desc_color( "\033[1;35min 5 minuites.\n\r", d );
                  close_socket( d, FALSE );
               }

               send_to_desc_color( "\n\r", d );

               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;36m                               New Character Name                               \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;37m Naming of your character is a very important aspect of playing The Fury .PvP.  \n\r", d );
               send_to_desc_color( "\033[0;37m make sure that the name fits in a medieval martial arts style of game. The     \n\r", d );
               send_to_desc_color( "\033[0;37m name should not contain profanity, be a common name or the name of an object,  \n\r", d );
               send_to_desc_color( "\033[0;37m it should also not be a name of a movie, book or other fictional character.    \n\r", d );
               send_to_desc_color( "\033[0;37m If your name does not meet these criteria, you will be asked to change it to   \n\r", d );
               send_to_desc_color( "\033[0;37m something more appropriate by The Fury .PvP. the game staff [GM] or [GS]       \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[1;37m Please enter the name of your new character: \n\r", d );
               d->newstate++;
               d->connected = CON_GET_NAME;
               return;
            }
            else
            {
               send_to_desc_color( "\033[1;35mSorry, %s does not pass the profanity filter, Please enter another name: \n\r", d );
               return;
            }
         }

         if( check_playing( d, argument, FALSE ) == BERR )
         {
            send_to_desc_color( "\033[1;37mName: ", d );
            return;
         }

         fOld = load_char_obj( d, argument, TRUE, FALSE );

         if( !d->character )
         {
            sprintf( log_buf, "Bad player file %s@%s.", argument, d->host );
            log_string( log_buf );
            send_to_desc_color( "\033[0;31mYour playerfile is corrupt...\033[1;31mPlease notify Tommi, \033[1;37meldhamud@gmail.com.\n\r", d );
            close_socket( d, FALSE );
            return;
         }

         ch = d->character;

         if( check_bans( ch, BAN_SITE ) )
         {
            send_to_desc_color( "\033[0;32mCONGRATULATIONS...\033[1;31mYour site has been banned from this Mud.\n\r", d );
            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 )
            {
               sprintf( buf, "Sorry, the name %s has been taken, Please enter another name: \n\r", argument );
               write_to_buffer( d, buf, 0 );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }

            send_to_desc_color( "\033[0;31mYou are denied access. \033[1;31mPlease contact eldhamud@gmail.com\n\r", d );

            close_socket( d, FALSE );
            return;
         }

         chk = check_reconnect( d, argument, FALSE );

         if( chk == BERR )
            return;

         if( chk )
         {
            fOld = TRUE;
         }
         else
         {
            if( wizlock && !IS_IMMORTAL( ch ) )
            {
               send_to_desc_color( "\033[1;35mThe game is wizlocked.  Only immortals can connect now.\n\r", d );
               send_to_desc_color( "\033[1;37mPlease try back later.\n\r", d );
               close_socket( d, FALSE );
               return;
            }
         }

         if( fOld )
         {
            if( d->newstate != 0 )
            {
               sprintf( buf, "\033[1;35mSorry, the name %s has been taken, Please enter another name: \n\r", argument );
               write_to_buffer( d, buf, 0 );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }

            send_to_desc_color( "\033[1;37mEnter Your Password: ", d );

            d->connected = CON_GET_OLD_PASSWORD;
            return;
         }
         else
         {
            if( d->newstate == 0 )
            {
               send_to_desc_color( "\n\r\033[1;35mNo such player exists.\n\r ", d );
               send_to_desc_color( "\033[1;35mPlease check your spelling or type \033[1;37mREGISTER \033[1;35mto start a new player.\n\r", d );
               send_to_desc_color( "\033[1;37mName: ", d );
               d->connected = CON_GET_NAME;
               d->character->desc = NULL;
               free_char( d->character ); /* Big Memory Leak before --Shaddai */
               d->character = NULL;
               return;
            }

            send_to_desc_color_args( d, "\033[1;37mDo you wish for your name to be, %s \033[1;35m(Y/N)\033[1;37m?\n\r", argument );

            d->connected = CON_CONFIRM_NEW_NAME;
            return;
         }

         break;

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

         if( str_cmp( smaug_crypt( argument ), ch->pcdata->pwd ) )
         {
            send_to_desc_color( "\033[0;31mYou have entered the wrong password, disconnecting.\n\r", d );
            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;

         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;

         strcpy( buf, ch->pcdata->filename );

         d->character->desc = NULL;

         free_char( d->character );

         d->character = NULL;

         fOld = load_char_obj( d, buf, FALSE, 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) has connected.", ch->name, d->host );

         if( ch->level < LEVEL_DEMI )
         {
            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 );
         }

         send_to_desc_color( "\033[1;37mPress ENTER", d );

         d->connected = CON_PRESS_ENTER;
         break;

      case CON_CONFIRM_NEW_NAME:

         switch ( *argument )
         {

            case 'y':

            case 'Y':
               send_to_desc_color( "\n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;36m                                     Password                                   \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;37m Your password should have a minimum of 8 characters and be a mixture of        \n\r", d );
               send_to_desc_color( "\033[0;37m numbers and letters. The less obvious the password is the more secure your     \n\r", d );
               send_to_desc_color( "\033[0;37m account will be and won't be easily guessed by anyone. Account security is     \n\r", d );
               send_to_desc_color( "\033[0;37m your responcibility.                                                           \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color_args( d, "\033[1;37m Please enter a password for %s: %s", ch->name, echo_off_str );
               d->connected = CON_GET_NEW_PASSWORD;
               break;

            case 'n':

            case 'N':
               send_to_desc_color( "\033[1;35mOk, what would you like your name to be: \n\r ", d );
               d->character->desc = NULL;
               free_char( d->character );
               d->character = NULL;
               d->connected = CON_GET_NAME;
               break;

            default:
               send_to_desc_color( "\033[1;35mPlease type Yes or No: \n\r ", d );
               break;
         }

         break;

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

         if( strlen( argument ) < 8 )
         {
            send_to_desc_color( "\033[1;35mThe minumum password lendth is 8 characters. Your password should\n\r", d );
            send_to_desc_color( "\033[1;35mcontain a mixture of Upper and Lower case letters and numbers.\n\r" "Password: ", d );
            return;
         }

         if( argument[0] == '!' )
         {
            send_to_desc_color( "\033[1;35mNew password cannot begin with the '!' character.\n\r", d );
            return;
         }

         pwdnew = smaug_crypt( argument );   /* MD5 Encryption */

         for( p = pwdnew; *p != STRING_NULL; p++ )
         {
            if( *p == '~' )
            {
               send_to_desc_color( "\033[0;32mNew password not acceptable, cannot use the \033[1;37m~ \033[0;32mcharacter.\n\rPassword:\n\r", d );
               return;
            }
         }

         DISPOSE( ch->pcdata->pwd );

         ch->pcdata->pwd = str_dup( pwdnew );
         send_to_desc_color( "\n\r\033[1;35mPlease retype the password to confirm:\n\r", d );
         d->connected = CON_CONFIRM_NEW_PASSWORD;
         break;

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

         if( str_cmp( smaug_crypt( argument ), ch->pcdata->pwd ) )
         {
            send_to_desc_color( "\033[1;35mPasswords don't match. \033[1;37mRetype password:\n\r", d );
            d->connected = CON_GET_NEW_PASSWORD;
            return;
         }

         send_to_desc_color( "\n\r", d );

         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;32m                                       \033[0;36mSex                            \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;37m Would you like your character to be male or female?                            \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color_args( d, "\033[0;37m Please enter the Sex of %s: ( M/F )? %s\n\r", ch->name, echo_on_str );
         d->connected = CON_GET_NEW_SEX;
         break;

      case CON_GET_NEW_SEX:

         switch ( argument[0] )
         {

            case 'm':

            case 'M':
               ch->sex = SEX_MALE;
               break;

            case 'f':

            case 'F':
               ch->sex = SEX_FEMALE;
               break;

            default:
               write_to_buffer( d, "\033[1;35mThat's not a sex. Would you like to be Male or Female?\n\r", 0 );
               return;
         }

         send_to_desc_color( "\n\r", d );

         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;36m                                      Race                                      \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;37m Race     :  Str+: Int+: Wis+: Dex+: Con+: Cha+: Lck+:  Hp+:  Ma+:  Alignment:  \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[1;37m Default  :     0     0     0     0     0     0     0     0    0     Good       \n\r", d );
         send_to_desc_color( "\033[1;37m You need to create your own races and edit the source in comm.c nanny function \n\r", d );
         send_to_desc_color( "\033[0;36m     \n\r", d );
         send_to_desc_color( "\033[0;36m     \n\r", d );
         send_to_desc_color( "\033[1;31m        \n\r", d );
         send_to_desc_color( "\033[1;31m       \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color_args( d, " \033[1;37mPlease enter the race of %s, or type help <name> for more information. \n\r", ch->name );
         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_PC_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 );
                  send_to_desc_color( "\033[1;35mPlease choose a race:\033[0;37m\n\r", d );
                  return;
               }
            }

            send_to_desc_color( "\033[1;35mNo help on that topic.  Please choose a race:\033[0;37m\n\r", d );

            return;
         }

         for( iRace = 0; iRace < MAX_PC_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_PC_RACE || !race_table[iRace]->race_name
             || race_table[iRace]->race_name[0] == STRING_NULL || IS_SET( race_table[iRace]->Class_restriction, 1 << ch->Class )
             || !str_cmp( race_table[iRace]->race_name, "unused" ) )
         {
            send_to_desc_color( "\033[1;35mThat's not a race. What would you like your race to be?\033[0;37m\n\r", d );
            return;
         }

         send_to_desc_color( "\n\r", d );

         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;36m                                     Class                                      \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;37m Class:        Faction:   School:   Weapon:     Armor:          Major Ability   \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[1;31m Default       Death      Fire      Blades      Brigandine      Physical        \n\r", d );
         send_to_desc_color( "\033[1;31m You need to create your own classes and edit the code in comm.c nanny function \n\r", d );
         send_to_desc_color( "\033[0;36m         \n\r", d );
         send_to_desc_color( "\033[0;36m        \n\r", d );
         send_to_desc_color( "\033[1;34m        \n\r", d );
         send_to_desc_color( "\033[1;34m         \n\r", d );
         send_to_desc_color( "\033[1;32m       \n\r", d );
         send_to_desc_color( "\033[1;32m         \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color_args( d, " \033[1;37mPlease enter the class of %s, or type help <name> for more information.\n\r", ch->name );
         d->connected = CON_GET_NEW_CLASS;
         break;

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

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

            for( Class = 0; Class < MAX_PC_CLASS; Class++ )
            {
               if( Class_table[Class]->who_name && Class_table[Class]->who_name[0] != '\0' )
               {
                  if( toupper( argument[0] ) == toupper( Class_table[Class]->who_name[0] ) && !str_prefix( argument, Class_table[Class]->who_name ) )
                  {
                     do_help( ch, argument );
                     send_to_desc_color( "\033[1;35mPlease choose a Class:\033[0;37m \n\r", d );
                     return;
                  }
               }
            }

            send_to_desc_color( "\033[1;35mSorry there is no such help topic for that class. Please choose a Class:\033[0;37m \n\r", d );

            return;
         }

         for( Class = 0; Class < MAX_PC_CLASS; Class++ )
         {
            if( Class_table[Class]->who_name && Class_table[Class]->who_name[0] != '\0' )
            {
               if( toupper( arg[0] ) == toupper( Class_table[Class]->who_name[0] ) && !str_prefix( arg, Class_table[Class]->who_name ) )
               {
                  ch->Class = Class;
                  break;
               }
            }
         }

         if( Class == MAX_PC_CLASS || !Class_table[Class]->who_name || Class_table[Class]->who_name[0] == '\0' || !str_cmp( Class_table[Class]->who_name, "unused" ) )
         {
            send_to_desc_color( "\033[1;35mThat's not a Class. Please enter the name of the Class you would like to be? \033[0;37m \n\r", d );
            return;
         }

         send_to_desc_color( "\n\r", d );

         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;36m                                   Ansi Color                                   \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
         send_to_desc_color( "\033[0;37m Would you like to start with ANSI Color turned on? Yes/No                      \n\r", d );
         send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );

         d->connected = CON_GET_WANT_RIPANSI;
         break;

      case CON_GET_WANT_RIPANSI:
         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, CHANNEL_MONITOR, "Monitor", LEVEL_IMMORTAL );
         ch->level = 0;
         ch->position = POS_STANDING;
         set_pager_color( AT_PLAIN, ch );

         switch ( argument[0] )
         {

            case 'y':

            case 'Y':
               xSET_BIT( ch->act, PLR_ANSI );
               send_to_char( "Press ENTER", ch );
               d->connected = CON_PRESS_ENTER;
               break;

            case 'n':

            case 'N':
               send_to_char( "Press ENTER", ch );
               d->connected = CON_PRESS_ENTER;
               break;

            default:
               send_to_desc_color( "\n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;36m                                   Ansi Color                                   \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               send_to_desc_color( "\033[0;37m Would you like to start with ANSI Color turned on? Yes/No                      \n\r", d );
               send_to_desc_color( "\033[0;32m--------------------------------------------------------------------------------\n\r", d );
               d->connected = CON_GET_WANT_RIPANSI;
               break;
         }

         break;

      case CON_PRESS_ENTER:

         if( IS_IMMORTAL( ch ) )
         {
            send_to_desc_color( "\n\r", d );
            do_last( ch, "10" );
            do_help( ch, "imotd" );
         }
         else
         {
            do_help( ch, "motd" );
         }

         d->connected = CON_READ_MOTD;

         break;

      case CON_READ_MOTD:
      {
         if( !IS_IMMORTAL( ch ) )
         {
            sprintf( buf, "\033[0;32m[&CANNOUNCEMENT\033[0;32m]&c %s has entered the game.", ch->name );
            talk_info( AT_GREEN, buf );

         }
      }

         add_char( ch );
         d->connected = CON_PLAYING;
         ch->desc->speed = 5;

         if( ch->level == 0 )
         {
            OBJ_DATA *obj;
            int iLang;
            ch->pcdata->clan = NULL;
            // need to sort out attribute prime here.......
            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;
            ch->alignment += race_table[ch->race]->alignment;
            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;

            for( iLang = 0; lang_array[iLang] != LANG_UNKNOWN; iLang++ )
               if( lang_array[iLang] == race_table[ch->race]->language )
                  break;

            if( lang_array[iLang] == LANG_UNKNOWN )
               bug( "Nanny: invalid racial language." );
            else
            {
               if( ( iLang = skill_lookup( lang_names[iLang] ) ) < 0 )
                  bug( "Nanny: cannot find racial language." );
               else
                  ch->pcdata->learned[iLang] = 100;
            }

            reset_colors( ch );

            ch->level = 1;
            ch->exp = 0;
            ch->max_hit += race_table[ch->race]->hit;
            ch->max_mana += race_table[ch->race]->mana;
            ch->hit = UMAX( 1, ch->max_hit );
            ch->mana = UMAX( 1, ch->max_mana );
            ch->move = ch->max_move;
            ch->practice = 10;   /*no more mr meany, better give them something to start with Tommi Aug 2005 */
            ch->gold = 5000;
            sprintf( buf, "Fighter of Fury .PvP." );
            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_AUTOLOOT );
            xSET_BIT( ch->act, PLR_AUTOGOLD );
            xSET_BIT( ch->act, PLR_AUTOEXIT );
            xSET_BIT( ch->act, PLR_COMPASS );
            xSET_BIT( ch->act, PLR_AUTOMAP );
            xSET_BIT( ch->act, PLR_AUTOSAC );
            SET_BIT( ch->pcdata->flags, PCFLAG_DEADLY );
            /*
             * Outfit all players in basic equipments Tommi.
             */

            if( ch->Class == CLASS_BUSHI || ch->Class == CLASS_SHUGENJA )
            {
               {
                  OBJ_INDEX_DATA *obj_ind = get_obj_index( 4014 );

                  if( obj_ind != NULL )
                  {
                     obj = create_object( obj_ind, 0 );
                     obj_to_char( obj, ch );
                     equip_char( ch, obj, WEAR_WIELD );
                  }
               }
            }
            else if( ch->Class == CLASS_WU_JEN || ch->Class == CLASS_KENSEI )
            {
               {
                  OBJ_INDEX_DATA *obj_ind = get_obj_index( 4012 );

                  if( obj_ind != NULL )
                  {
                     obj = create_object( obj_ind, 0 );
                     obj_to_char( obj, ch );
                     equip_char( ch, obj, WEAR_WIELD );
                  }
               }
            }
            else if( ch->Class == CLASS_IAIJUTSU || ch->Class == CLASS_HENSHIN )
            {
               {
                  OBJ_INDEX_DATA *obj_ind = get_obj_index( 4016 );

                  if( obj_ind != NULL )
                  {
                     obj = create_object( obj_ind, 0 );
                     obj_to_char( obj, ch );
                     equip_char( ch, obj, WEAR_WIELD );
                  }
               }
            }
            else if( ch->Class == CLASS_KISHI || ch->Class == CLASS_SOHEI )
            {
               {
                  OBJ_INDEX_DATA *obj_ind = get_obj_index( 4018 );

                  if( obj_ind != NULL )
                  {
                     obj = create_object( obj_ind, 0 );
                     obj_to_char( obj, ch );
                     equip_char( ch, obj, WEAR_WIELD );
                  }
               }
            }

            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 );
            }
         }
         else if( !IS_IMMORTAL( ch ) && ch->pcdata->release_date > 0 && ch->pcdata->release_date > current_time )
         {
            if( ch->in_room->vnum == 6 || ch->in_room->vnum == 8 || ch->in_room->vnum == 1206 )
               char_to_room( ch, ch->in_room );
            else
               char_to_room( ch, get_room_index( 8 ) );
         }
         else if( ch->in_room && ( IS_IMMORTAL( ch ) || !xIS_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 );

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

         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 );
         }
	 send_to_char("Welcome to The Fury .PvP. Let the fury begin", ch );
         do_look( ch, "auto" );
         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;
   }

   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 != STRING_NULL; 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.\n\rName: ", 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... Welcome back.\n\r", ch );
            do_look( ch, "auto" );
            act( AT_ACTION, "$n has reconnected.", ch, NULL, NULL, TO_CANSEE );
            sprintf( log_buf, "%s (%s) reconnected.", ch->name, d->host );
            log_string_plus( log_buf, LOG_COMM, UMAX( sysdata.log_level, ch->level ) );
            d->connected = CON_PLAYING;
         }

         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;
         ch = dold->original ? dold->original : dold->character;

         if( !ch->name || ( cstate != CON_PLAYING && cstate != CON_EDITING ) )
         {
            write_to_buffer( d, "Already connected - try again.\n\r", 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.\n\r", 0 );
         write_to_buffer( dold, "Kicking off old connection... bye!\n\r", 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... Welcome back.\n\r", 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 ) );

         /*
          * if ( ch->level < LEVEL_SAVIOR )
          * to_channel( log_buf, CHANNEL_MONITOR, "Monitor", 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;
    */

   if( !ch || !ch->desc || ch->desc->connected != CON_PLAYING || !IS_IDLE( ch ) )
      return;

   /*
    * if ( IS_IMMORTAL(ch) )
    * return;
    */
   ch->timer = 0;
   was_in_room = ch->was_in_room;
   char_from_room( ch );
   char_to_room( ch, was_in_room );
   ch->was_in_room = ch->in_room;

   /*
    * ch->was_in_room = NULL;
    */
   REMOVE_BIT( ch->pcdata->flags, PCFLAG_IDLE );
   act( AT_ACTION, "$n has returned from the void.", ch, NULL, NULL, TO_ROOM );
   return;
}

/*
 * 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;
}


#define NAME(ch)        (IS_NPC(ch) ? ch->short_descr : ch->name)
char *act_string( const char *format, CHAR_DATA * to, CHAR_DATA * ch, void *arg1, 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 *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;

   if( str[0] == '$' )
      DONT_UPPER = FALSE;

   while( *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;

            case 't':
               i = ( char * )arg1;
               break;

            case 'T':
               i = ( char * )arg2;
               break;

            case 'n':
               i = ( to ? PERS( ch, to, TRUE ) : NAME( ch ) );
               break;

            case 'N':
               i = ( to ? PERS( vch, to, FALSE ) : NAME( vch ) );
               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;
   }

   strcpy( point, "\n\r" );

   if( !DONT_UPPER )
      buf[0] = UPPER( buf[0] );

   return buf;
}

#undef NAME
void act( short AType, const char *format, CHAR_DATA * ch, void *arg1, void *arg2, int type )
{
   char *txt;
   CHAR_DATA *to;
   CHAR_DATA *vch = ( CHAR_DATA * ) arg2;
   /*
    * Discard null and zero-length messages.
    */

   if( !format || format[0] == STRING_NULL )
      return;

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

   if( !ch->in_room )
      to = NULL;
   else if( type == TO_CHAR )
      to = ch;
   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;
   }

   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;

      if( type == TO_CHAR )
      {
         if( to != ch )
            continue;

         if( !is_same_map( ch, to ) )
            continue;
      }

      if( type == TO_VICT && ( to != vch || to == ch ) )
         continue;

      if( type == TO_ROOM )
      {
         if( to == ch )
            continue;

         if( !is_same_map( ch, to ) )
            continue;
      }

      if( type == TO_NOTVICT )
      {
         if( to == ch || to == vch )
            continue;

         if( !is_same_map( ch, to ) )
            continue;
      }

      if( type == TO_CANSEE )
      {
         if( to == ch )
            continue;

         if( IS_IMMORTAL( ch ) && IS_PLR_FLAG( ch, PLR_WIZINVIS ) )
         {
            if( to->level < ch->pcdata->wizinvis )
               continue;
         }

         if( !is_same_map( ch, to ) )
            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 )
      {
         set_char_color( AType, to );
         send_to_char( 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 )
   {
      send_to_char( "Huh?\n\r", ch );
      return;
   }

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

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

   if( !str_cmp( ch->name, argument ) )
   {
      send_to_char( "That's already your name!\n\r", 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.\n\r", 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.\n\r", 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.\n\r", ch );
   ch->pcdata->auth_state = 0;
   return;
}

char *default_fprompt( CHAR_DATA * ch )
{
   static char buf[60];
   strcpy( buf, "\033[1;31m<\033[1;33m%hhp &C%mm &G%vmv\033[1;31m %E> " );

   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, "\033[1;31m<\033[1;33m%hhp &C%mm &G%vmv\033[1;31m> " );

   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;
   int 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] != STRING_NULL )
      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, ANSI_RESET );
      d->prevcolor = 0x08;
      pbuf += 4;
   }

   /*
    * Clear out old color stuff
    */
   for( ; *prompt; prompt++ )
   {
      /*
       * '%' = prompt commands
       * Note: foreground changes will revert background to 0 (black)
       */
      if( *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 '%':
            *pbuf = STRING_NULL;
            pstat = 0x80000000;

            switch ( *prompt )
            {

               case '%':
                  *pbuf++ = '%';
                  *pbuf = STRING_NULL;
                  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( !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( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;

                     if( percent >= 100 )
                        strcpy( pbuf, "perfect health" );
                     else if( percent >= 90 )
                        strcpy( pbuf, "slightly scratched" );
                     else if( percent >= 80 )
                        strcpy( pbuf, "few bruises" );
                     else if( percent >= 70 )
                        strcpy( pbuf, "some cuts" );
                     else if( percent >= 60 )
                        strcpy( pbuf, "several wounds" );
                     else if( percent >= 50 )
                        strcpy( pbuf, "nasty wounds" );
                     else if( percent >= 40 )
                        strcpy( pbuf, "bleeding freely" );
                     else if( percent >= 30 )
                        strcpy( pbuf, "covered in blood" );
                     else if( percent >= 20 )
                        strcpy( pbuf, "leaking guts" );
                     else if( percent >= 10 )
                        strcpy( pbuf, "almost dead" );
                     else
                        strcpy( pbuf, "DYING" );
                  }

                  break;

               case 'c':

                  if( !IS_IMMORTAL( ch ) )
                     break;

                  if( !ch->fighting || ( victim = ch->fighting->who ) == NULL )
                     strcpy( pbuf, "N/A" );
                  else
                  {
                     if( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;

                     if( percent >= 100 )
                        strcpy( pbuf, "perfect health" );
                     else if( percent >= 90 )
                        strcpy( pbuf, "slightly scratched" );
                     else if( percent >= 80 )
                        strcpy( pbuf, "few bruises" );
                     else if( percent >= 70 )
                        strcpy( pbuf, "some cuts" );
                     else if( percent >= 60 )
                        strcpy( pbuf, "several wounds" );
                     else if( percent >= 50 )
                        strcpy( pbuf, "nasty wounds" );
                     else if( percent >= 40 )
                        strcpy( pbuf, "bleeding freely" );
                     else if( percent >= 30 )
                        strcpy( pbuf, "covered in blood" );
                     else if( percent >= 20 )
                        strcpy( pbuf, "leaking guts" );
                     else if( percent >= 10 )
                        strcpy( pbuf, "almost dead" );
                     else
                        strcpy( pbuf, "DYING" );
                  }

                  break;

               case 'e':

                  if( ( victim = who_fighting( ch ) ) != NULL )
                  {
                     if( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;

                     if( percent >= 65 )
                        sprintf( pbuf, " \033[1;31mEnemy: &g%d%%", percent );
                     else if( percent >= 25 && percent < 65 )
                        sprintf( pbuf, " \033[1;31mEnemy: \033[1;33m%d%%", percent );
                     else
                        sprintf( pbuf, " \033[1;31mEnemy: \033[0;31m%d%%", percent );
                  }

                  break;

               case 'E':

                  if( ( victim = who_fighting( ch ) ) != NULL )
                  {
                     if( victim->max_hit > 0 )
                        percent = ( 100 * victim->hit ) / victim->max_hit;
                     else
                        percent = -1;

                     if( percent >= 100 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g++++\033[1;31m]&D" );
                     else if( percent >= 90 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g+++ \033[1;31m]&D" );
                     else if( percent >= 80 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g++  \033[1;31m]&D" );
                     else if( percent >= 70 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++&g+   \033[1;31m]&D" );
                     else if( percent >= 58 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+++    \033[1;31m]&D" );
                     else if( percent >= 45 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m++     \033[1;31m]&D" );
                     else if( percent >= 30 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++\033[1;33m+      \033[1;31m]&D" );
                     else if( percent >= 28 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+++&x       \033[1;31m]&D" );
                     else if( percent >= 15 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m++&x        \033[1;31m]&D" );
                     else if( percent >= 8 )
                        strcpy( pbuf, "\033[1;31mEnemy: [\033[0;31m+&x         \033[1;31m]&D" );
                     else
                        strcpy( pbuf, "\033[1;31mEnemy: [          \033[1;31m]&D" );
                  }

                  break;

               case 'h':
                  pstat = ch->hit;
                  break;

               case 'H':
                  pstat = ch->max_hit;
                  break;

               case 'm':
                  pstat = ch->mana;
                  break;

               case 'M':
                  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':
                  pstat = 0;

                  break;

               case 'B':
                  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 'r':
                  if( IS_IMMORTAL( och ) )
                     pstat = ch->in_room->vnum;

                  break;

               case 'F':
                  if( IS_IMMORTAL( och ) )
                     sprintf( pbuf, "%s", ext_flag_string( &ch->in_room->room_flags, r_flags ) );

                  break;

               case 'R':
                  if( xIS_SET( och->act, PLR_ROOMVNUM ) )
                     sprintf( pbuf, "\033[1;31m<#%d>&D ", ch->in_room->vnum );

                  break;

               case 'x':
                  pstat = ch->exp;

                  break;

               case 'X':
                  pstat = exp_level( ch, ch->level + 1 ) - ch->exp;

                  break;

               case 'o':  /* display name of object on auction */
                  if( auction->item )
                     strcpy( pbuf, auction->item->name );

                  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 = STRING_NULL;

   send_to_char( buf, ch );
   send_to_char( "\n\r", ch );
   return;
}

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 'n':
         lines = 0;
         pclines = 0x7FFFFFFF;   /* As many lines as possible */
         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->descriptor, 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->descriptor, ANSI_LBLUE, 0 ) == FALSE )
         return FALSE;

   if( ( ret = write_to_descriptor( d->descriptor, "(C)ontinue, (N)on-stop, (R)efresh, (B)ack, (Q)uit: [C] ", 0 ) ) == FALSE )
      return FALSE;

   if( xIS_SET( ch->act, PLR_ANSI ) )
   {
      char buf[32];
      sprintf( buf, "%s", color_str( d->pagecolor, ch ) );
      ret = write_to_descriptor( d->descriptor, buf, 0 );
   }

   return ret;
}

void do_speed( CHAR_DATA * ch, char *argument )
{
   short speed = atoi( argument );

   if( !ch->desc )
      return;  /* Don't send messages to people who don't exist. duh. */

   if( argument[0] == STRING_NULL )
   {
      ch_printf( ch, "Your present speed is a %d, which equates to %d bytes per second.\n\r", ch->desc->speed, client_speed( ch->desc->speed ) );
      return;
   }

   if( speed > 8 || speed < 0 )
   {
      send_to_char( "Speed is between 0 and 8.\n\r", ch );
      return;
   }

   ch->desc->speed = speed;

   ch_printf( ch, "The MUD will now send output to you at %d bytes per second.\n\r", client_speed( speed ) );

   if( client_speed( speed ) > 2048 )
      ch_printf( ch, "You should be aware %d is fast enough to lag you if you have a slow connection.\n\r", client_speed( speed ) );

   return;
}

short client_speed( short speed )
{
   switch ( speed )
   {

      default:
         break;

      case 1:
         return 512;

      case 2:
         return 1024;

      case 3:
         return 2048;

      case 4:
         return 3072;

      case 5:
         return 4096;

      case 6:
         return 5120;

      case 7:
         return 6144;

      case 8:
         return 7168;

   }

   return 512; /* Better than a mere default case. */
}

// const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, STRING_NULL };
// const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, STRING_NULL };
   const char echo_new_str[] = { IAC, SB, 102 };

void do_telnet( CHAR_DATA * ch, char *argument )
{
  char buf[MAX_STRING_LENGTH];
  sprintf( buf, " %s %s: %s \n\r",echo_on_str ,argument ,echo_on_str );
  write_to_buffer( ch->desc, buf, 0 );
  ch_printf(ch, "%s sent", echo_new_str);
  return;
}