/
Sapphire/bin/
Sapphire/db/
Sapphire/db/OLC_rooms/
Sapphire/db/abi/
Sapphire/db/em_src/
Sapphire/db/helps/
Sapphire/db/helps/emman/ifunc/
Sapphire/db/npcs/Tatt/
Sapphire/db/objects/Tatt/
Sapphire/db/q_data/
Sapphire/db/rooms/Tatt/
Sapphire/doc/
Sapphire/doc/em/
Sapphire/etc/
Sapphire/src/abic/
Sapphire/src/areacon/
Sapphire/src/client/
Sapphire/src/embc/
Sapphire/src/emi/
Sapphire/src/emi/test/
Sapphire/src/include/
Sapphire/src/sapphire/em/
Sapphire/src/tcon/
/*
 * Copyright (C) 1995-1997 Christopher D. Granz
 *
 * This header may not be removed.
 *
 * Refer to the file "License" included in this package for further
 * information and before using any of the following.
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <netdb.h>
#include <time.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#include <arpa/inet.h>

#include "sapphire.h"


/*
 * Prototypes
 */

#define CD                                                     CHAR_DATA

static bool      illegal_name                                ( char * );
static CD *      new_guest                                     ( void );

#undef CD


/*
 * Functions
 */

/*
 * Init the telnet socket.
 */
void init_telnet_socket( int iPort )
{
    struct sockaddr_in sSA;
    int iX = 1;

    if ( ( sTelnetControl = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
        sap_fatal( "Create socket: %s.", strerror( errno ) );

    if ( setsockopt( sTelnetControl, SOL_SOCKET, SO_REUSEADDR,
      (char *) &iX, sizeof( iX ) ) < 0 )
        sap_fatal( "Set socket option: %s.", strerror( errno ) );

    zero_out( &sSA, sizeof( sSA ) );
    sSA.sin_family = AF_INET;
    sSA.sin_port   = htons( iPort );

    if ( bind( sTelnetControl, (struct sockaddr *) &sSA, sizeof( sSA ) )
      < 0 )
        sap_fatal( "Bind: %s.", strerror( errno ) );

    if ( listen( sTelnetControl, 3 ) < 0 )
        sap_fatal( "Listen: %s.", strerror( errno ) );
}


/*
 * Init the binary socket.
 */
void init_binary_socket( int iPort )
{
    struct sockaddr_in sSA;
    int iX = 1;

    if ( ( sBinaryControl = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
        sap_fatal( "Create socket: %s.", strerror( errno ) );

    if ( setsockopt( sBinaryControl, SOL_SOCKET, SO_REUSEADDR,
      (char *) &iX, sizeof( iX ) ) < 0 )
        sap_fatal( "Set socket option: %s.", strerror( errno ) );

    zero_out( &sSA, sizeof( sSA ) );
    sSA.sin_family = AF_INET;
    sSA.sin_port   = htons( iPort );

    if ( bind( sBinaryControl, (struct sockaddr *) &sSA, sizeof( sSA ) )
      < 0 )
        sap_fatal( "Bind: %s.", strerror( errno ) );

    if ( listen( sBinaryControl, 3 ) < 0 )
        sap_fatal( "Listen: %s.", strerror( errno ) );
}


/*
 * Accept a new connection.
 */
void accept_connection( void )
{
    TERM_DATA *pTermNew;
    struct sockaddr_in sSock;
    struct hostent *pHost;
    char cBuf[MAX_STRING];
    char *pBuf;
    int iSize;
    sapsocket sSocket;

    iSize   = sizeof( sSock );
    sSocket = accept( sTelnetControl, (struct sockaddr *) &sSock,
                &iSize );

    if ( sSocket < 0 )
    {
        sap_error( "Accept: %s.", strerror( errno ) );
        return;
    }

    if ( fcntl( sSocket, F_SETFL, FNDELAY ) < 0 )
    {
        sap_error( "Set descriptor option: %s.", strerror( errno ) );
        close( sSocket );
        return;
    }

    if ( pTermFree == NULL )
        pTermNew             = alloc_mem( sizeof( *pTermNew ) );
    else
    {
        pTermNew             = pTermFree;
        pTermFree            = pTermFree->pNext;
    }

    pTermNew->sTelnetSocket  = sSocket;
    pTermNew->sBinarySocket  = -1;
    pTermNew->iTermType      = CLIENT_GENERIC;
    pTermNew->iOutBufferSize = 2048;
    pTermNew->iInBufferSize  = ( MAX_INPUT * 4 );
    pTermNew->pOutBuffer     = alloc_mem( pTermNew->iOutBufferSize );
    pTermNew->pOutLast       = alloc_mem( MAX_STRING );
    pTermNew->pInBuffer      = alloc_mem( pTermNew->iInBufferSize );
    pTermNew->pInCommand     = alloc_mem( MAX_INPUT );
    pTermNew->iConState      = CON_MAIN_MENU;
    pTermNew->iBinaryMode    = BIN_MODE_FILENAME;
    pTermNew->pCurrentDir    = EMPTY_STRING;
    pTermNew->bLocalEcho     = FALSE;
    pTermNew->iPageLines     = 22;

    if ( getpeername( sSocket, (struct sockaddr *) &sSock, &iSize ) < 0 )
    {
        sap_error( "Get peer name: %s.", strerror( errno ) );
        pBuf                 = NULL;
        pHost                = NULL;
        pTermNew->pHostname  = str_dup( "(unknown)" );
    }
    else
    {
        pBuf                 = inet_ntoa( sSock.sin_addr );

        pHost           = gethostbyaddr( (char *) &sSock.sin_addr.s_addr,
                            sizeof( sSock.sin_addr.s_addr ), AF_INET );
        pTermNew->pHostname  = str_dup( ( pHost ? pHost->h_name : pBuf ) );
    }

    snprintf( cBuf, MAX_STRING, "\n  Connection established with: %s",
      pTermNew->pHostname );

    if ( pHost != NULL && pBuf != NULL )
    {
        strcat( cBuf, "\n                               (" );
        strcat( cBuf, pBuf );
        strcat( cBuf, ")" );
    }

    lprintf( cBuf );

    pTermNew->pNext = pTermList;
    pTermList       = pTermNew;

    term_send_string( pTermNew, "%sDo you have local echo on? ",
      COPYRIGHT_NOTICE );
    pTermNew->iConState         = CON_SAPPHIRE_IDENT;
}


/*
 * Accept a Sapphire compatible client.
 */
void accept_binary_connection( void )
{
    TERM_DATA *pTerm;
    struct sockaddr_in sSock;
    struct hostent *pHost;
    char cBuf[MAX_STRING];
    char *pBuf;
    char *pBuf2;
    int iSize;
    sapsocket sSocket;

    iSize   = sizeof( sSock );
    sSocket = accept( sBinaryControl, (struct sockaddr *) &sSock,
                &iSize );

    if ( sSocket < 0 )
    {
        sap_error( "Accept: %s.", strerror( errno ) );
        return;
    }

    if ( fcntl( sSocket, F_SETFL, FNDELAY ) < 0 )
    {
        sap_error( "Set descriptor option: %s.", strerror( errno ) );
        close( sSocket );
        return;
    }

    if ( getpeername( sSocket, (struct sockaddr *) &sSock, &iSize ) < 0 )
    {
        sap_error( "Get peer name: %s.", strerror( errno ) );
        close( sSocket );
        return;
    }
    else
    {
        pBuf  = inet_ntoa( sSock.sin_addr );
        pHost = gethostbyaddr( (char *) &sSock.sin_addr.s_addr,
                  sizeof( sSock.sin_addr.s_addr ), AF_INET );
        pBuf2 = ( pHost != NULL ? pHost->h_name : pBuf );
    }

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        if ( pTerm->iConState == CON_SAPPHIRE_IDENT
          && strcmp( pTerm->pHostname, pBuf2 ) == 0 )
            break;
    }

    if ( pTerm == NULL )
    {
        snprintf( cBuf, MAX_STRING, "\n\r"
          "Server: This port is used for binary transfers only; "
          "please use port %d.\n\r\n\r", iTelnetPort );

        iSize   = strlen( cBuf );

        if ( write( sSocket, cBuf, iSize ) < 0 )
            sap_error( "Write: %s.", strerror( errno ) );

        close( sSocket );
        return;
    }

    snprintf( cBuf, MAX_STRING,
      "\n  Sapphire compatible client identified for: %s",
      pTerm->pHostname );

    if ( pHost != NULL && pBuf != NULL )
    {
        strcat( cBuf,
          "\n                                             (" );
        strcat( cBuf, pBuf );
        strcat( cBuf, ")" );
    }

    lprintf( cBuf );

    pTerm->iTermType     = CLIENT_SAPPHIRE_COMPATIBLE;
    pTerm->sBinarySocket = sSocket;
    pTerm->bLocalEcho    = TRUE;
    pTerm->iPageLines    = 10;
    pTerm->iConState     = CON_GET_NAME;

    term_send_string( pTerm, "\n\r\n\r"
      "[Sapphire compatible client identified]\n\r\n\r%s"
      "\n\rBy what name do you wish to be known? ", pGreeting );

    write_file( pTerm, get_file_type( pLoginScreenFilename ),
      pLoginScreenFilename );
}


/*
 * Close a connection.
 */
void close_connection( TERM_DATA **ppTermClose )
{
    TERM_DATA *pTermClose = *ppTermClose;
    TERM_DATA *pTerm;
    CHAR_DATA *pChar;

    if ( pTermClose->pChildP != NULL )
    {
        kill( pTermClose->pChildP->pPid, SIGKILL );
        pTermClose->pChildP = NULL;
    }

    if ( pTermClose->pOutBuffer[0] != '\0'
      || pTermClose->pBinData != NULL )
        flush_buffer( *ppTermClose );

    pChar = pTermClose->pChar;

    if ( pTermClose->pControlled != NULL )
    {
        pTermClose->pControlled->pTerm = NULL;

        if ( pChar != NULL )
        {
            char_to_room( pChar, uDefaultRoom.pRoom );
            send_game_message( "~h appears out of nothingness.",
              MESSAGE_DEST_ROOM, TRUE, pChar, NULL, pChar->pInRoom,
              TRUE, NUMBER_POSITION_RESTING, pChar );
        }
    }

    if ( pChar != NULL )
    {
        if ( !IS_GUEST( pChar ) )
            iNumberPlayers--;

        if ( pTermClose->iConState == CON_PLAYING
          && !IS_GUEST( pChar ) )
        {
            send_game_message( "~c's image shimers for a moment.",
              MESSAGE_DEST_ROOM, TRUE, pChar, NULL, pChar->pInRoom, FALSE,
              NUMBER_POSITION_RESTING, pChar );
            pChar->pTerm = NULL;
        }
        else
            free_char( &pChar );
    }

    if ( pTermClose == pTermList )
        pTermList         = pTermClose->pNext;
    else
    {
        for ( pTerm = pTermList; pTerm && pTerm->pNext != pTermClose;
          pTerm = pTerm->pNext );

#ifdef DEBUG
        if ( pTerm != NULL )
            pTerm->pNext  = pTermClose->pNext;
        else
            sap_debug( "Terminal not found." );
#else
        pTerm->pNext      = pTermClose->pNext;
#endif
    }

    lprintf( "\n  Closing connection to: %s", pTermClose->pHostname );
    close( pTermClose->sTelnetSocket );

    if ( pTermClose->iTermType == CLIENT_SAPPHIRE_COMPATIBLE )
        close( pTermClose->sBinarySocket );

    free_mem( (void **) &pTermClose->pOutBuffer );
    free_mem( (void **) &pTermClose->pInBuffer );
    free_mem( (void **) &pTermClose->pInCommand );

    if ( pTermClose->pEditString != NULL )
        free_mem( (void **) &pTermClose->pEditString );

    str_free( pTermClose->pHostname );
    str_free( pTermClose->pCurrentDir );
    zero_out( pTermClose, sizeof( *pTermClose ) );

    /*
     * Recycling of terminal sturctures in vital in the current code,
     * (it expects that it can still access a terminal structure even
     * after it is no longer in use) if you remove the recycling code
     * you can bet that there will be problems.
     */
    pTermClose->pNext     = pTermFree;
    pTermFree             = pTermClose;

    *ppTermClose          = NULL;
}


/*
 * Reads one line of input from the descriptor and stores it in
 * the input buffer (pInBuffer).
 */
bool read_string( TERM_DATA *pTerm )
{
    int i, iRead        = 0;

    if ( pTerm->pInCommand[0] != '\0' )
        return ( TRUE );

    i                   = strlen( pTerm->pInBuffer );

    if ( i >= ( pTerm->iInBufferSize - 1 ) )
    {
        lprintf( "%s input overflow!", pTerm->pHostname );
        write_string_buffer( pTerm, "\n\r\aServer: Input overflow!\n\r" );
        return ( FALSE );
    }

    for ( ; ; )
    {
        iRead           = read( pTerm->sTelnetSocket,
                            ( pTerm->pInBuffer + i ),
                            ( pTerm->iInBufferSize - i - 1 ) );

        if ( iRead > 0 )
        {
            i          += iRead;

            if ( pTerm->pInBuffer[i - 1] == '\n'
              || pTerm->pInBuffer[i - 1] == '\r' )
            {
                if ( pTerm->bLocalEcho != TRUE )
                    write_string_buffer( pTerm, "\n\r" );

                break;
            }
        }
        else if ( iRead == 0 )
            return ( FALSE );
        else if ( errno == EAGAIN /* EWOULDBLOCK */ )
            break;
        else if ( errno == EINTR )
            continue;
        else
        {
            /* sap_error( "Read: %s.", strerror( errno ) ); */
            return ( FALSE );
        }
    }

    pTerm->pInBuffer[i] = '\0';
    return ( TRUE );
}


bool read_binary_data( TERM_DATA *pTerm )
{
    int i     = 0;
    int iRead = 0;

    for ( ; ; )
    {
        iRead = read( pTerm->sTelnetSocket, &i, 1 );

        if ( iRead > 0 )
        {
            /* For now we do processing right here. */
            switch ( i )
            {
              case BIN_CMD_FILENAME_MODE:
                  pTerm->iBinaryMode = BIN_MODE_FILENAME;
                  break;

              case BIN_CMD_FILE_MODE    :
                  pTerm->iBinaryMode = BIN_MODE_FILE;
                  break;

              default                   : break;
            }
        }
        else if ( iRead == 0 )
            return ( FALSE );
        else if ( errno == EAGAIN /* EWOULDBLOCK */ )
            break;
        else if ( errno == EINTR )
            continue;
        else
        {
            /* sap_error( "Read: %s.", strerror( errno ) ); */
            return ( FALSE );
        }
    }

    return ( TRUE );
}


void read_string_buffer( TERM_DATA *pTerm )
{
    int i     = 0;
    int iComm = 0;

    if ( pTerm->pInCommand[0] != '\0' )
        return;

    while ( pTerm->pInBuffer[i] != '\n' && pTerm->pInBuffer[i] != '\r' )
    {
        if ( pTerm->pInBuffer[i++] == '\0' )
            return;
    }

    i         = 0;

    while ( pTerm->pInBuffer[i] != '\n' && pTerm->pInBuffer[i] != '\r' )
    {
        if ( i >= ( MAX_INPUT - 2 ) )
        {
            write_string( pTerm, "Server: Line too long.\n\r" );
            pTerm->pInBuffer[i++] = '\n';
            pTerm->pInBuffer[i]   = '\0';
            break;
        }

        if ( pTerm->pInBuffer[i] == '\b' && iComm > 0 )
            iComm--;
        else if ( isascii( pTerm->pInBuffer[i] ) )
            pTerm->pInCommand[iComm++] = pTerm->pInBuffer[i];

        i++;
    }

    if ( iComm == '\0' )
        pTerm->pInCommand[iComm++]     = ' ';

    pTerm->pInCommand[iComm]           = '\0';

    while ( pTerm->pInBuffer[i] == '\n' || pTerm->pInBuffer[i] == '\r' )
        i++;

    for ( iComm = 0; pTerm->pInBuffer[iComm] != '\0'; iComm++ )
        pTerm->pInBuffer[iComm]        = pTerm->pInBuffer[i + iComm];
}


bool write_string( TERM_DATA *pTerm, char *pText )
{
    int iLength = strlen( pText );

    do
    {
        errno = 0;
        write( pTerm->sTelnetSocket, pText, iLength );
    }
    while ( errno == EINTR );

    if ( errno != 0 )
    {
        sap_error( "Write: %s.", strerror( errno ) );
        return ( FALSE );
    }

    return ( TRUE );
}


bool write_binary_data( TERM_DATA *pTerm )
{
    int iWrite;

    do
    {
        errno  = 0;
        iWrite = write( pTerm->sBinarySocket, pTerm->pBinTransfering,
          ( pTerm->lBinRemainSize > MAX_BIN_TRANSFER_BLOCK
          ? MAX_BIN_TRANSFER_BLOCK : pTerm->lBinRemainSize ) );
    }
    while ( errno == EINTR );

    if ( errno != 0 )
    {
        sap_error( "Write: %s.", strerror( errno ) );
        return ( FALSE );
    }

    pTerm->pBinTransfering += iWrite;
    pTerm->lBinRemainSize  -= iWrite;

    return ( TRUE );
}


void clear_binary_transfer( TERM_DATA *pTerm )
{
    if ( pTerm->pBinData == NULL )
        return;

    pTerm->pBinTransfering = NULL;
    pTerm->lBinRemainSize  = 0;
    free_mem( (void **) &pTerm->pBinData );
}


void write_string_buffer( TERM_DATA *pTerm, char *pText )
{
    int iLength               = strlen( pText );
    int iOutSize;

    if ( iLength >= MAX_STRING )
    {
        sap_error( "Terminal buffer: String too long." );
        return;
    }

    strcpy( pTerm->pOutLast, pText );
    iOutSize              = strlen( pTerm->pOutBuffer );

    /*
     * Expand the buffer as needed.
     */
    while ( iOutSize + iLength >= pTerm->iOutBufferSize )
    {
        int iNewSize;

        if ( pTerm->iOutBufferSize > MAX_OUTBUF_SIZE )
        {
            sap_error( "Terminal buffer: Overflow; closing." );
            close_connection( &pTerm );
            return;
        }

        iNewSize          = ( pTerm->iOutBufferSize << 1 ); /* Times 2 */
        pTerm->pOutBuffer = realloc_mem( pTerm->pOutBuffer, iNewSize );
        pTerm->iOutBufferSize = iNewSize;
    }

    strcpy( pTerm->pOutBuffer + iOutSize, pText );
}


/*
 * Begins sending a file to a Sapphire compatible client.
 */
void write_file( TERM_DATA *pTerm, intt iFileType, char *pFilename )
{
    int i;

    if ( pTerm->pBinData != NULL )
        return;

    switch ( pTerm->iBinaryMode )
    {
      case BIN_MODE_FILENAME:
          i                      = ( strlen( pFilename ) + 3 );
          pTerm->pBinData        = alloc_mem( i );

          pTerm->pBinData[0]     = BIN_CMD_SENDING_FILENAME;
          pTerm->pBinData[1]     = iFileType;
          strcpy( (char *) &pTerm->pBinData[2], pFilename );

          pTerm->pBinTransfering = pTerm->pBinData;
          pTerm->lBinRemainSize  = i;
          break;

      case BIN_MODE_FILE    :
          break;

      default               : break;
    }
}


void rewrite_string_buffer( TERM_DATA *pTerm )
{
    write_string_buffer( pTerm, pTerm->pOutLast );
}


/*
 * Force a write of all data in the string buffer and binary buffer.
 */
void flush_buffer( TERM_DATA *pTerm )
{
    if ( pTerm->iTermType != CLIENT_SAPPHIRE_COMPATIBLE )
    {
        if ( pTerm->pOutBuffer[0] == '\0' )
            return;

        write_string( pTerm, pTerm->pOutBuffer );
        pTerm->pOutBuffer[0] = '\0';
    }
    else
    {
        if ( pTerm->pOutBuffer[0] != '\0' )
        {
            write_string( pTerm, pTerm->pOutBuffer );
            pTerm->pOutBuffer[0] = '\0';
        }

        if ( pTerm->pBinData != NULL )
            clear_binary_transfer( pTerm );
    }
}


void term_send_string( TERM_DATA *pTerm, char *pFormat, ... )
{
    va_list pArgs;
    char cStr[MAX_STRING];

    if ( pTerm == NULL )
        return;

    va_start( pArgs, pFormat );

    vsnprintf( cStr, MAX_STRING, pFormat, pArgs );
    write_string_buffer( pTerm, cStr );

    va_end( pArgs );
}


/*
 * Send a string to a character.
 */
void send_string( CHAR_DATA *pChar, char *pFormat, ... )
{
    va_list pArgs;
    char cStr[MAX_STRING];

    if ( pChar == NULL || pChar->pTerm == NULL
      || ( pChar->pTerm->iConState != CON_PLAYING
      && pChar->pTerm->iConState != CON_NPC_EDITOR
      && pChar->pTerm->iConState != CON_OBJECT_EDITOR
      && pChar->pTerm->iConState != CON_ROOM_EDITOR ) )
        return;

    va_start( pArgs, pFormat );

    vsnprintf( cStr, MAX_STRING, pFormat, pArgs );
    write_string_buffer( pChar->pTerm, cStr );

    va_end( pArgs );
}


/*
 * Send a formated message to multiple destinations.
 */
void send_game_message( char *pFormat, long lDest, ... )
{
    ROOM_INDEX_DATA *pRoom;
    CHAR_DATA *pChar;
    CHAR_DATA *pChar2;
    CHAR_DATA *pCharBuf;
    va_list vlVarArgs;
    va_list vlBak;
    char cBuf[MAX_STRING] = "";
    int i;
    int iPos;
    bool bTrigger;
    bool bHide;

    va_start( vlVarArgs, lDest );

    switch ( lDest )
    {
      case MESSAGE_DEST_CHAR  :
          pChar       = va_arg( vlVarArgs, CHAR_DATA * );
          iPos        = va_arg( vlVarArgs, long );
#ifdef DEBUG
          if ( pChar == NULL )
          {
              wcdebug( "Null character." );
              break;
          }
#endif
          if ( pChar->iPosition < iPos )
              break;

          expand_game_symbols( pFormat, cBuf, pChar, vlVarArgs );
          send_string( pChar, "%s\n\r", cBuf );

          break;

      case MESSAGE_DEST_VICTOM:
          bTrigger    = ( va_arg( vlVarArgs, long ) > 0 ? TRUE : FALSE );
          pChar       = va_arg( vlVarArgs, CHAR_DATA * );
          pChar2      = va_arg( vlVarArgs, CHAR_DATA * );
          iPos        = va_arg( vlVarArgs, long );
#ifdef DEBUG
          if ( pChar == NULL || pChar2 == NULL )
          {
              wcdebug( "Null character." );
              break;
          }
#endif
          if ( pChar2->iPosition < iPos )
              break;

          expand_game_symbols( pFormat, cBuf, pChar2, vlVarArgs );

          if ( pChar2->pTerm != NULL
            && ( ( i = ( strlen( pChar2->pTerm->pOutBuffer ) - 2 ) ) < 0
            || pChar2->pTerm->pOutBuffer[i] != '\n' ) )
              send_string( pChar2, "\n\r%s\n\r", cBuf );
          else
              send_string( pChar2, "%s\n\r", cBuf );

          if ( bTrigger == TRUE )
              check_char_trigger( NUMBER_NPC_TRIGGER_MESSAGE, pChar, NULL,
                pChar2, cBuf );

          break;

      case MESSAGE_DEST_ROOM  :
          bTrigger    = ( va_arg( vlVarArgs, long ) > 0 ? TRUE : FALSE );
          pChar       = va_arg( vlVarArgs, CHAR_DATA * );
          pChar2      = va_arg( vlVarArgs, CHAR_DATA * );
          pRoom       = va_arg( vlVarArgs, ROOM_INDEX_DATA * );
          bHide       = va_arg( vlVarArgs, long );
          iPos        = va_arg( vlVarArgs, long );
          vlBak       = vlVarArgs;
#ifdef DEBUG
          if ( pRoom == NULL )
          {
              wcdebug( "Null room." );
              break;
          }
#endif
          if ( bTrigger == TRUE )
          {
              expand_game_symbols( pFormat, cBuf, NULL, vlVarArgs );
              check_room_trigger( NUMBER_ROOM_TRIGGER_MESSAGE, pChar,
                pRoom, cBuf );

              for ( pCharBuf = pRoom->pPeople; pCharBuf != NULL;
                pCharBuf = pCharBuf->pNextInRoom )
              {
                  if ( pCharBuf != pChar && pCharBuf != pChar2
                    && pCharBuf->iPosition >= iPos )
                  {
                      if ( bHide == TRUE
                        && visable_char( pChar, pCharBuf ) != TRUE )
                          continue;

                      vlVarArgs = vlBak;
                      expand_game_symbols( pFormat, cBuf, pCharBuf,
                        vlVarArgs );

                      if ( pCharBuf->pTerm != NULL
                        && ( ( i = strlen( pCharBuf->pTerm->pOutBuffer )
                        - 2 ) < 0 || pCharBuf->pTerm->pOutBuffer[i]
                        != '\n' ) )
                          send_string( pCharBuf, "\n\r%s\n\r", cBuf );
                      else
                          send_string( pCharBuf, "%s\n\r", cBuf );

                      check_char_trigger( NUMBER_NPC_TRIGGER_MESSAGE,
                        pChar, NULL, pCharBuf, cBuf );
                  }
              }
          }
          else
          {
              for ( pCharBuf = pRoom->pPeople; pCharBuf != NULL;
                pCharBuf = pCharBuf->pNextInRoom )
              {
                  if ( pCharBuf != pChar && pCharBuf != pChar2
                    && pCharBuf->iPosition >= iPos )
                  {
                      if ( bHide == TRUE
                        && visable_char( pChar, pCharBuf ) != TRUE )
                          continue;

                      vlVarArgs = vlBak;
                      expand_game_symbols( pFormat, cBuf, pCharBuf,
                        vlVarArgs );

                      if ( pCharBuf->pTerm != NULL
                        && ( ( i = strlen( pCharBuf->pTerm->pOutBuffer )
                        - 2 ) < 0 || pCharBuf->pTerm->pOutBuffer[i]
                        != '\n' ) )
                          send_string( pCharBuf, "\n\r%s\n\r", cBuf );
                      else
                          send_string( pCharBuf, "%s\n\r", cBuf );
                  }
              }
          }

          break;

#ifdef DEBUG
      default                 :
          wcdebug( "Unknown destination." );
          break;
#endif
    }


    va_end( vlVarArgs );
}


void expand_game_symbols( char *p, char *pBuf, CHAR_DATA *pChar,
                          va_list vlVarArgs )
{
    CHAR_DATA *pCharBuf;
    OBJ_DATA *pObj;
    char *pBak             = pBuf;
    char *pStr;
    long l;

    /*
     * I know that having two code blocks that are almost identical is
     * a bit messy but this function is called offen and I don't want to
     * slow it down with having to do checks all over the place.
     */
    if ( pChar == NULL )
        goto room;

    while ( *p != '\0' )
    {
        if ( *p == '~' )
        {
            switch ( *++p )
            {
              case '~':
                  *pBuf++   = '~';
                  break;

              case 'c':
                  pCharBuf  = va_arg( vlVarArgs, CHAR_DATA * );
#ifdef DEBUG
                  if ( pCharBuf->pInRoom == NULL )
                  {
                      wcdebug( "Character not in room." );
                      break;
                  }
#endif
                  if ( visable_char( pCharBuf, pChar ) != TRUE )
                      pStr  = "someone";
                  else if ( knows_char( pChar, pCharBuf ) == TRUE )
                  {
                      if ( IS_NPC( pCharBuf ) )
                      {
                          if ( IS_SET( pCharBuf->pNPCData->fNPCFlags,
                            FLAG_NPC_SPECIAL ) )
                              pStr  = pCharBuf->pNPCData->sShortDesc;
                          else if ( count_npcs( pCharBuf->pInRoom,
                            pCharBuf->pNPCData ) < 2 )
                          {
                              pStr  = pCharBuf->pNPCData->sShortDesc;
                              strcpy( pBuf, "the " );
                              pBuf += 4;
                          }
                          else
                          {
                              pStr  = pCharBuf->pNPCData->sShortDesc;
                              strcpy( pBuf, "a " );
                              pBuf += 2;
                          }
                      }
                      else
                          pStr      = pCharBuf->pPCData->sName;
                  }
                  else if ( ( count_race( pCharBuf->pInRoom,
                    pCharBuf->iRace ) - ( pChar->iRace == pCharBuf->iRace
                    ? 1 : 0 ) ) < 2 )
                  {
                      pStr  = uncapit(
                                get_race_string( pCharBuf->iRace ) );
                      strcpy( pBuf, "the " );
                      pBuf += 4;
                  }
                  else
                      pStr  = uncapit(
                                get_race_string_2( pCharBuf->iRace ) );

                  strcpy( pBuf, pStr );
                  pBuf     += strlen( pStr );
                  break;

              case 'h':
                  pCharBuf  = va_arg( vlVarArgs, CHAR_DATA * );
#ifdef DEBUG
                  if ( pCharBuf->pInRoom == NULL )
                  {
                      wcdebug( "Character not in room." );
                      break;
                  }
#endif
                  if ( visable_char( pCharBuf, pChar ) != TRUE )
                      pStr  = "someone";
                  else if ( knows_char( pChar, pCharBuf ) == TRUE )
                  {
                      if ( IS_NPC( pCharBuf ) )
                      {
                          if ( IS_SET( pCharBuf->pNPCData->fNPCFlags,
                            FLAG_NPC_SPECIAL ) )
                              pStr  = pCharBuf->pNPCData->sShortDesc;
                          else
                          {
                              pStr  = pCharBuf->pNPCData->sShortDesc;
                              strcpy( pBuf, "a " );
                              pBuf += 2;
                          }
                      }
                      else
                          pStr      = pCharBuf->pPCData->sName;
                  }
                  else
                      pStr  = uncapit(
                                get_race_string_2( pCharBuf->iRace ) );

                  strcpy( pBuf, pStr );
                  pBuf     += strlen( pStr );
                  break;

              case 's':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  pStr     = ( pCharBuf->iSex == NUMBER_SEX_MALE ? "his"
                             : "hers" );
                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'e':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf,
                    ( pCharBuf->iSex == NUMBER_SEX_MALE ? "his"
                    : "her" ) );
                  pBuf    += 3;
                  break;

              case 'x':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf,
                    ( pCharBuf->iSex == NUMBER_SEX_MALE ? "him"
                    : "her" ) );
                  pBuf    += 3;
                  break;

              case 'r':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf, get_race_string( pCharBuf->iRace ) );
                  pBuf    += 3;
                  break;

              case 'g':
                  break;

              case 'o':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( visable_object( pObj, pChar ) != TRUE )
                      pStr = "something";
                  else if ( IS_SET( pObj->fObjFlags,
                    FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "a " );
                      pBuf += 2;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'b':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( visable_object( pObj, pChar ) != TRUE )
                      pStr = "something";
                  else if ( IS_SET( pObj->fObjFlags,
                    FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "the " );
                      pBuf += 4;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'j':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( visable_object( pObj, pChar ) != TRUE )
                      pStr = "something";
                  else if ( IS_SET( pObj->fObjFlags,
                    FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "your " );
                      pBuf += 5;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

#ifdef DEBUG
              default :
                  wcdebug( "Unknown symbol `~%c'.", *p );
                  break;
#endif
            }
        }
        else if ( *p == '$' )
        {
            switch ( *++p )
            {
              case '$':
                  *pBuf++  = '$';
                  break;

              case 'i':
                  l        = va_arg( vlVarArgs, long );
                  sprintf( pBuf, "%ld", l );

                  while ( *pBuf != '\0' )
                      pBuf++;

                  break;

              case 's':
                  pStr     = va_arg( vlVarArgs, char * );
                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

#ifdef DEBUG
              default :
                  wcdebug( "Unknown symbol `$%c'.", *p );
                  break;
#endif
            }
        }
        else
            *pBuf++        = *p;

        p++;
    }

    while ( !isalpha( *pBak ) && *pBak != '\0' )
        pBak++;

    *pBak                  = UPPER( *pBak );
    return;

room:
    while ( *p != '\0' )
    {
        if ( *p == '~' )
        {
            switch ( *++p )
            {
              case '~':
                  *pBuf++   = '~';
                  break;

              case 'c':
                  pCharBuf  = va_arg( vlVarArgs, CHAR_DATA * );
#ifdef DEBUG
                  if ( pCharBuf->pInRoom == NULL )
                  {
                      wcdebug( "Character not in room." );
                      break;
                  }
#endif
                  if ( IS_NPC( pCharBuf ) )
                  {
                      if ( IS_SET( pCharBuf->pNPCData->fNPCFlags,
                        FLAG_NPC_SPECIAL ) )
                          pStr  = pCharBuf->pNPCData->sShortDesc;
                      else if ( count_npcs( pCharBuf->pInRoom,
                        pCharBuf->pNPCData ) < 2 )
                      {
                          pStr  = pCharBuf->pNPCData->sShortDesc;
                          strcpy( pBuf, "the " );
                          pBuf += 4;
                      }
                      else
                      {
                          pStr  = pCharBuf->pNPCData->sShortDesc;
                          strcpy( pBuf, "a " );
                          pBuf += 2;
                      }
                  }
                  else
                      pStr      = pCharBuf->pPCData->sName;

                  strcpy( pBuf, pStr );
                  pBuf     += strlen( pStr );
                  break;

              case 'h':
                  pCharBuf  = va_arg( vlVarArgs, CHAR_DATA * );
#ifdef DEBUG
                  if ( pCharBuf->pInRoom == NULL )
                  {
                      wcdebug( "Character not in room." );
                      break;
                  }
#endif
                  if ( IS_NPC( pCharBuf ) )
                  {
                      if ( IS_SET( pCharBuf->pNPCData->fNPCFlags,
                        FLAG_NPC_SPECIAL ) )
                          pStr  = pCharBuf->pNPCData->sShortDesc;
                      else
                      {
                          pStr  = pCharBuf->pNPCData->sShortDesc;
                          strcpy( pBuf, "a " );
                          pBuf += 2;
                      }
                  }
                  else
                      pStr      = pCharBuf->pPCData->sName;

                  strcpy( pBuf, pStr );
                  pBuf     += strlen( pStr );
                  break;

              case 's':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  pStr     = ( pCharBuf->iSex == NUMBER_SEX_MALE ? "his"
                             : "hers" );
                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'e':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf,
                    ( pCharBuf->iSex == NUMBER_SEX_MALE ? "his"
                    : "her" ) );
                  pBuf    += 3;
                  break;

              case 'x':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf,
                    ( pCharBuf->iSex == NUMBER_SEX_MALE ? "him"
                    : "her" ) );
                  pBuf    += 3;
                  break;

              case 'r':
                  pCharBuf = va_arg( vlVarArgs, CHAR_DATA * );
                  strcpy( pBuf, get_race_string( pCharBuf->iRace ) );
                  pBuf    += 3;
                  break;

              case 'g':
                  break;

              case 'o':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( IS_SET( pObj->fObjFlags, FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "a " );
                      pBuf += 2;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'b':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( IS_SET( pObj->fObjFlags, FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "the " );
                      pBuf += 4;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

              case 'j':
                  pObj     = va_arg( vlVarArgs, OBJ_DATA * );

                  if ( IS_SET( pObj->fObjFlags, FLAG_OBJECT_SPECIAL ) )
                      pStr = pObj->sShortDesc;
                  else
                  {
                      pStr  = pObj->sShortDesc;
                      strcpy( pBuf, "your " );
                      pBuf += 5;
                  }

                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

#ifdef DEBUG
              default :
                  wcdebug( "Unknown symbol `~%c'.", *p );
                  break;
#endif
            }
        }
        else if ( *p == '$' )
        {
            switch ( *++p )
            {
              case '$':
                  *pBuf++  = '$';
                  break;

              case 'i':
                  l        = va_arg( vlVarArgs, long );
                  sprintf( pBuf, "%ld", l );

                  while ( *pBuf != '\0' )
                      pBuf++;

                  break;

              case 's':
                  pStr     = va_arg( vlVarArgs, char * );
                  strcpy( pBuf, pStr );
                  pBuf    += strlen( pStr );
                  break;

#ifdef DEBUG
              default :
                  wcdebug( "Unknown symbol `$%c'.", *p );
                  break;
#endif
            }
        }
        else
            *pBuf++        = *p;

        p++;
    }

    while ( !isalpha( *pBak ) && *pBak != '\0' )
        pBak++;

    *pBak                  = UPPER( *pBak );
}


bool process_output( TERM_DATA *pTerm )
{
    CHAR_DATA *pChar;
#if 0
    CHAR_DATA *pVictim;
#endif
    if ( bRunning )
    {
        if ( pTerm->pPageString != NULL )
        {
            term_send_string( pTerm,
              "\n\r%s[Press Enter to continue]%s ",
              make_ansi_code( ANSI_CODEREF_SGR_DSET, "0;44", NULL,
              pTerm ), make_ansi_code( ANSI_CODEREF_SGR_DSET, "0", NULL,
              pTerm ) );
            goto end;
        }

        if ( pTerm->ppEditString != NULL )
        {
            if ( pTerm->pOutBuffer[0] == '\0' )
                term_send_string( pTerm, "Edit %ld: ", pTerm->iEditLine );
            else
                term_send_string( pTerm, "\n\rEdit %ld: ",
                  pTerm->iEditLine );

            goto end;
        }

        switch ( pTerm->iConState )
        {
          case CON_PLAYING      :
              pChar = ( pTerm->pControlled != NULL ? pTerm->pControlled
                      : pTerm->pChar );
#if 0
              if ( ( pVictim = pChar->pFighting ) != NULL )
              {
                  int iPercent;

                  if ( pVictim->pStats->iStatHP > 0 )
                      iPercent = ( pVictim->iHP * 100
                                 / pVictim->pStats->iStatHP );
                  else
                      iPercent = -1;

                  if ( IS_NPC( pVictim ) )
                  {
                      if ( !IS_SET( pVictim->pNPCData->fNPCFlags,
                        FLAG_NPC_SPECIAL ) )
                      {
                          strcpy( cBuf, "The " );
                          strcat( cBuf, pVictim->pNPCData->sShortDesc );
                      }
                      else
                          strcpy( cBuf, pVictim->pNPCData->sShortDesc );
                  }
                  else
                      strcpy( cBuf, pVictim->pPCData->sName );

                  cBuf[0]      = UPPER( cBuf[0] );
                  iPercent     = round( iPercent, 15 );

                  switch ( iPercent )
                  {
                    case 100:
                        strcat( cBuf, "is in excellent condition.\n\r" );
                        break;

                    case 85 :
                        strcat( cBuf,
                          "has a few cuts and scratches.\n\r" );
                        break;

                    case 70 :
                        strcat( cBuf, "has some slight wounds.\n\r" );
                        break;

                    case 55 :
                        strcat( cBuf, "has quite a few wounds.\n\r" );
                        break;

                    case 40 :
                        strcat( cBuf, "has many large wounds.\n\r" );
                        break;

                    case 25 :
                        strcat( cBuf, "looks very weak.\n\r" );
                        break;

                    case 10 :
                        strcat( cBuf, "is in awful condition.\n\r" );
                        break;

                    default :
                        strcat( cBuf, "is bleeding to death.\n\r" );
                        break;
                  }

                  strcat( cBuf, "\n\r" );
                  write_string_buffer( pTerm, cBuf );
              }
#endif
              term_send_string( pTerm, "\n\r%s ",
                format_prompt( pChar, pTerm->pChar->pPCData->sPrompt ) );
              break;

          case CON_NPC_EDITOR   :
              if ( pTerm->uEdit.pEditNPC != NULL )
                  term_send_string( pTerm, "\n\r[NPC Edit: %ld]> ",
                    pTerm->uEdit.pEditNPC->iNumber );
              else
                  term_send_string( pTerm, "\n\r[NPC Edit]> " );

              break;

          case CON_OBJECT_EDITOR:
              if ( pTerm->uEdit.pEditObject != NULL )
                  term_send_string( pTerm, "\n\r[Object Edit: %ld]> ",
                    pTerm->uEdit.pEditObject->iNumber );
              else
                  term_send_string( pTerm, "\n\r[Object Edit]> " );

              break;

          case CON_ROOM_EDITOR  :
              if ( pTerm->uEdit.pEditRoom != NULL )
                  term_send_string( pTerm, "\n\r[Room Edit: %ld]> ",
                    pTerm->uEdit.pEditRoom->iNumber );
              else
                  term_send_string( pTerm, "\n\r[Room Edit]> " );

              break;

          case CON_SYSTEM_SHELL :
              term_send_string( pTerm, "%sssh:~/%s$%s ",
                make_ansi_code( ANSI_CODEREF_SGR_DSET, "0;33", NULL,
                pTerm ), pTerm->pCurrentDir, make_ansi_code(
                ANSI_CODEREF_SGR_DSET, "0", NULL, pTerm ) );
              break;

          default               :
              break;
        }
    }

end:
    if ( pTerm->pOutBuffer[0] == '\0' )
        return ( TRUE );

    if ( write_string( pTerm, pTerm->pOutBuffer ) == FALSE )
        return ( FALSE );
    else
    {
        pTerm->pOutBuffer[0] = '\0';
        return ( TRUE );
    }
}


/*
 * Deals with input from terminals that have not logged in yet.
 */
void process_login( TERM_DATA *pTerm, char *pArg )
{
    extern ROOM_INDEX_DATA *pStartingRoom;
    TERM_DATA *pTerm2;
    CHAR_DATA *pChar;
    char cArg[MAX_INPUT];
    char cBuf[MAX_STRING];
    char *pName;
    int i, i2, i3;

    one_arg( pArg, cArg );
    cBuf[0]   = '\0';
    pChar     = pTerm->pChar;

    if ( pChar != NULL )
        pName = pChar->pPCData->sName;
    else
        pName = EMPTY_STRING;

    switch ( pTerm->iConState )
    {
      case ( CON_SAPPHIRE_IDENT ):
          pTerm->iConState                  = CON_GET_LOCAL_ECHO_ON;
          process_login( pTerm, pArg );
          break;

      case ( CON_GET_LOCAL_ECHO_ON ):
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm,
                "\n\rPlease answer yes or no: " );
              break;
          }

          if ( str_prefix( cArg, "Yes" ) == TRUE )
              pTerm->bLocalEcho             = TRUE;
          else if ( str_prefix( cArg, "No" ) == TRUE )
              pTerm->bLocalEcho             = FALSE;
          else
          {
              term_send_string( pTerm,
                "\n\rPlease answer yes or no: " );
              break;
          }

          term_send_string( pTerm, "\n\rDoes your client have full "
            "ANSI support (not just color)? " );
          pTerm->iConState                  = CON_GET_CLIENT_TYPE;

          break;

      case ( CON_GET_CLIENT_TYPE ):
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm,
                "\n\rPlease answer yes or no: " );
              break;
          }

          if ( str_prefix( cArg, "Yes" ) == TRUE )
              pTerm->iTermType              = CLIENT_ANSI_COMPATIBLE;
          else if ( str_prefix( cArg, "No" ) == TRUE )
              pTerm->iTermType              = CLIENT_GENERIC;
          else
          {
              term_send_string( pTerm,
                "\n\rPlease answer yes or no: " );
              break;
          }

          term_send_string( pTerm, "%s\n\r", make_ansi_code(
            ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ) );

          if ( iMode == MODE_RPG )
          {
              term_send_string( pTerm,
                "%s\n\rBy what name do you wish to be known? ",
                pGreeting );
              pTerm->iConState              = CON_GET_NAME;
          }
          else
          {
              term_send_string( pTerm,
                "__ Main Menu __\n\r"
                "\n\r"
                "  1) Login\n\r"
                "  2) Disconnect\n\r"
                "  3) Player List\n\r"
                "\n\r"
                "Choose an option: " );
              pTerm->iConState              = CON_MAIN_MENU;
          }

          break;

      case ( CON_MAIN_MENU )   :
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rChoose an option: " );
              break;
          }

          switch ( atoi( cArg ) )
          {
            case  1:
                term_send_string( pTerm,
                  "%s\n\r%s\n\rBy what name do you wish to be known? ",
                  make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                  pTerm ), pGreeting );
                pTerm->iConState            = CON_GET_NAME;
                break;

            case  2:
                term_send_string( pTerm, "Farewell.\n\r" );
                close_connection( &pTerm );
                break;

            case  3:
                cBuf[0]                     = '\0';
                setup_string_pager( pTerm );

                for ( pTerm2 = pTermList; pTerm2; pTerm2 = pTerm2->pNext )
                {
                    if ( pTerm2->pChar != NULL
                      && pTerm2->iConState == CON_PLAYING
                      && !IS_GUEST( pTerm2->pChar ) )
                    {
                        sprintf( cBuf, "%s\n\r",
                          pTerm2->pChar->pPCData->sName );
                        page_string( pTerm, cBuf );
                    }
                }

                if ( cBuf[0] == '\0' )
                {
                    term_send_string( pTerm,
                      "No one is playing at the moment.\n\r" );
                }

                finish_string_pager( pTerm );
                term_send_string( pTerm, "\n\rChoose an option: " );
                break;

            default:
                term_send_string( pTerm, "That is not a valid option.\n\r"
                  "\n\rChoose an option: " );
                break;
          }

          break;

      case ( CON_GET_NAME )    :
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rName: " );
              break;
          }

          if ( illegal_name( cArg ) == TRUE )
          {
              term_send_string( pTerm,
                "\n\rNo, that name will not do.\n\r" );
              term_send_string( pTerm,
                "\n\rPlease choose a different name: " );
              return;
          }

          if ( iMode == MODE_RPG && str_compare( cArg, "Guest" ) == TRUE )
          {
              pChar                         = new_guest( );
              pTerm->pChar                  = pChar;
              pChar->pTerm                  = pTerm;
              term_send_string( pTerm,
                "%s\n\r%s\n\r%s[Press Enter to continue]%s ",
                make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                pTerm ), pMOTD, make_ansi_code( ANSI_CODEREF_SGR_DSET,
                "0;44", NULL, pTerm ), make_ansi_code(
                ANSI_CODEREF_SGR_DSET, "0", NULL, pTerm ) );
              pTerm->iConState              = CON_READ_MOTD;
              return;
          }

          /*
           * (PCs are always at 0 in the hash list.)
           */
          for ( pChar = ppCharList[0]; pChar; pChar = pChar->pNext )
          {
              if ( !IS_NPC( pChar ) &&
                str_compare( pChar->pPCData->sName, cArg ) == TRUE )
              {
                  pTerm->pChar              = pChar;
                  term_send_string( pTerm, "\n\rWhat is the password? " );
                  pTerm->iConState          = CON_RECONNECT;
                  return;
              }
          }

          iNumberPlayers++;

          if ( ( pChar = load_pc( cArg ) ) == NULL )
          {
              if ( iNumberPlayers > iMaxPlayers )
              {
                  term_send_string( pTerm,
                    "\n\rServer: Sorry, the maximum number of players for"
                    "this server has been exceeded.\n\r        "
                    "Please try again later.\n\r" );
                  close_connection( &pTerm );
                  break;
              }

              pChar                         = new_pc( );
              pChar->pPCData->sName = save_string( cap_first( cArg ) );
              pTerm->pChar                  = pChar;
              pChar->pTerm                  = pTerm;
              term_send_string( pTerm,
                "\n\rPlease enter the character creation code: " );
              pTerm->iConState              = CON_GET_CREATION_CODE;
              break;
          }

          pTerm->pChar                      = pChar;
          pChar->pTerm                      = pTerm;

          if ( pChar->iLevel < BUILDER_LEVEL
            && iNumberPlayers > iMaxPlayers )
          {
              term_send_string( pTerm,
                "\n\rServer: Sorry, the maximum number of players for"
                "this server has been exceeded.\n\r        "
                "Please try again later.\n\r" );
              close_connection( &pTerm );
              break;
          }

          term_send_string( pTerm, "\n\rWhat is the password? " );
          pTerm->iConState                  = CON_GET_PASSWORD;
          break;

      case ( CON_GET_CREATION_CODE ):
          if ( strcmp( cCreationCode, pArg ) != 0 )
          {
              term_send_string( pTerm, "\n\r"
                "That is not the correct character creation code.\n\r" );
              close_connection( &pTerm );
              return;
          }

          /*
           * Enter character creation.
           */
          term_send_string( pTerm,
            "\n\rEntering character creation system.\n\r" );
          term_send_string( pTerm,
            "\n\rGive me a password for this character: " );
          pTerm->iConState                  = CON_GET_PASSWORD;
          break;

      case ( CON_GET_PASSWORD ):
          if ( pChar->iLevel > 0 )
          {
              char *pPass = pChar->pPCData->sPassword;

              if ( pPass[0] != '\0'
                && strcmp( pPass, crypt( pArg, pPass ) ) )
              {
                  term_send_string( pTerm,
                    "\n\rI now see that you are not who you say you are "
                    "... Goodbye.\n\r" );
                  pChar->pInRoom = NULL;
                  close_connection( &pTerm );
                  return;
              }

              term_send_string( pTerm,
                "%s\n\r%s\n\r%s[Press Enter to continue]%s ",
                make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                pTerm ), pMOTD, make_ansi_code( ANSI_CODEREF_SGR_DSET,
                "0;44", NULL, pTerm ), make_ansi_code(
                ANSI_CODEREF_SGR_DSET, "0", NULL, pTerm ) );
              pTerm->iConState              = CON_READ_MOTD;
          }
          else
          {
              i                             = strlen( pArg );

              if ( i < 5 )
              {
                  term_send_string( pTerm, "\n\r"
                    "A password must be aleast 5 characters long.\n\r"
                    "\n\rPassword: " );
                  break;
              }

              if ( i > 25 )
              {
                  term_send_string( pTerm, "\n\r"
                    "A password may be no more then 25 characters long."
                    "\n\r\n\rPassword: " );
                  break;
              }

              pChar->pPCData->sPassword = save_string(
                                            crypt( pArg, pName ) );

              /*
               * Goto the character creation menu.
               */
              term_send_string( pTerm, "%s"
                "\n\r__ Character Creation Menu __\n\r"
                "\n\r"
                "  1) Choose race.                       Race: %s\n\r"
                "  2) Roll stats.                Stats rolled: %s\n\r"
                "  3) Choose sex.                         Sex: %s\n\r"
                "  4) Finish creation.\n\r"
                "\n\r"
                "Choose an option: ", make_ansi_code(
                ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
                ( pChar->iRace == 0 ? "None"
                : get_race_string( pChar->iRace ) ),
                ( pChar->pStats->iStatStr == 0 ? "No" : "Yes" ),
                ( pChar->iSex == 0 ? "None"
                : get_sex_string( pChar->iSex ) ) );
              pTerm->iConState              = CON_CREATION_MENU;
          }

          break;

      case ( CON_CREATION_MENU ):
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rChoose an option: " );
              break;
          }

          switch ( atoi( cArg ) )
          {
            case  1:
                for ( i = 0; rRaceTable[i].pName[0] != '\0'; i++ )
                {
                    if ( rRaceTable[i].bPCRace == TRUE )
                    {
                        strcat( cBuf, " " );
                        strcat( cBuf, rRaceTable[i].pName );
                    }
                }

                term_send_string( pTerm, "%s\n\rThe following races are "
                  "available:\n\r  %s\n\r\n\rRace: ",
                  make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                  pTerm ), cBuf );
                pTerm->iConState            = CON_GET_RACE;
                break;

            case  2:
                if ( pChar->iRace == 0 )
                {
                    term_send_string( pTerm,
                      "You must select your race first.\n\r"
                      "\n\rChoose an option: " );
                    break;
                }

                roll_stats( pChar->pStats );
                modify_stats( pChar->pStats, pChar->iRace );
                term_send_string( pTerm, "%s\n\r"
                  " Strength        : %d\n\r"
                  " Intelligence    : %d\n\r"
                  " Wisdom          : %d\n\r"
                  " Dexterity       : %d\n\r"
                  " Constitution    : %d\n\r"
                  " Charisma        : %d\n\r"
                  " Luck            : %d\n\r"
                  "\n\r"
                  " Hit Points      : %d\n\r"
                  " Magic Points    : %d\n\r"
                  " Movement Points : %d\n\r"
                  "\n\rAre these stats OK? ", make_ansi_code(
                  ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
                  pChar->pStats->iStatStr,
                  pChar->pStats->iStatInt,
                  pChar->pStats->iStatWis,
                  pChar->pStats->iStatDex,
                  pChar->pStats->iStatCon,
                  pChar->pStats->iStatCha,
                  pChar->pStats->iStatLuc,
                  pChar->pStats->iStatHP,
                  pChar->pStats->iStatMP,
                  pChar->pStats->iStatMV );
                pTerm->iConState            = CON_ROLL_STATS;
                break;

            case 3:
                term_send_string( pTerm, "%s\n\rSex (Male/Female)? ",
                  make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                  pTerm ) );
                pTerm->iConState            = CON_GET_SEX;
                break;

            case 4:
                if ( pChar->iRace == 0 || pChar->pStats->iStatStr == 0
                  || pChar->iSex == 0 )
                {
                    term_send_string( pTerm,
                      "You must fill in all the fields first.\n\r"
                      "\n\rChoose an option: " );
                    break;
                }
#if 0
                if ( pChar->iRace == 0 )
                {
                    term_send_string( pTerm,
                      "You need to select a race first.\n\r"
                      "\n\rChoose an option: " );
                    break;
                }

                if ( pChar->pStats->iStatStr == 0 )
                {
                    term_send_string( pTerm,
                      "You need to roll some stats first.\n\r"
                      "\n\rChoose an option: " );
                    break;
                }

                if ( pChar->iSex == 0 )
                {
                    term_send_string( pTerm,
                      "You need to select a sex first.\n\r"
                      "\n\rChoose an option: " );
                    break;
                }
#endif
                term_send_string( pTerm,
                  "%s\n\r%s\n\r%s[Press Enter to continue]%s ",
                  make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                  pTerm ), pMOTD, make_ansi_code( ANSI_CODEREF_SGR_DSET,
                  "0;44", NULL, pTerm ), make_ansi_code(
                  ANSI_CODEREF_SGR_DSET, "0", NULL, pTerm ) );
                pTerm->iConState            = CON_READ_MOTD;
                break;

            default:
                term_send_string( pTerm, "That is not a valid option.\n\r"
                  "\n\rChoose an option: " );
                break;
          }

          break;

      case ( CON_GET_RACE )    :
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rRace: " );
              break;
          }

          for ( i = 0; rRaceTable[i].pName[0] != '\0'; i++ )
          {
              if ( str_prefix( cArg, rRaceTable[i].pName ) == TRUE
                && rRaceTable[i].bPCRace == TRUE )
              {
                  pChar->iRace              = rRaceTable[i].iNumber;
                  pChar->fActFlags          = rRaceTable[i].fActFlags;
                  pChar->pStats->iStatStr   = 0;

                  /*
                   * Goto the character creation menu.
                   */
                  term_send_string( pTerm, "%s"
                    "\n\r__ Character Creation Menu __\n\r"
                    "\n\r"
                    "  1) Choose race.                       Race: %s\n\r"
                    "  2) Roll stats.                Stats rolled: %s\n\r"
                    "  3) Choose sex.                         Sex: %s\n\r"
                    "  4) Finish creation.\n\r"
                    "\n\r"
                    "Choose an option: ", make_ansi_code(
                    ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
                    ( pChar->iRace == 0 ? "None"
                    : get_race_string( pChar->iRace ) ),
                    ( pChar->pStats->iStatStr == 0 ? "No" : "Yes" ),
                    ( pChar->iSex == 0 ? "None"
                    : get_sex_string( pChar->iSex ) ) );
                  pTerm->iConState          = CON_CREATION_MENU;
                  return;
              }
          }

          term_send_string( pTerm,
            "\n\rI do not know the race you are refering to.\n\r"
            "\n\rRace: " );
          break;

      case ( CON_ROLL_STATS )  :
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rAre these stats OK? " );
              break;
          }

          if ( str_prefix( cArg, "No" ) == TRUE )
          {
              roll_stats( pChar->pStats );
              modify_stats( pChar->pStats, pChar->iRace );
                term_send_string( pTerm, "%s\n\r"
                  " Strength        : %d\n\r"
                  " Intelligence    : %d\n\r"
                  " Wisdom          : %d\n\r"
                  " Dexterity       : %d\n\r"
                  " Constitution    : %d\n\r"
                  " Charisma        : %d\n\r"
                  " Luck            : %d\n\r"
                  "\n\r"
                  " Hit Points      : %d\n\r"
                  " Magic Points    : %d\n\r"
                  " Movement Points : %d\n\r"
                  "\n\rAre these stats OK? ", make_ansi_code(
                  ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
                  pChar->pStats->iStatStr,
                  pChar->pStats->iStatInt,
                  pChar->pStats->iStatWis,
                  pChar->pStats->iStatDex,
                  pChar->pStats->iStatCon,
                  pChar->pStats->iStatCha,
                  pChar->pStats->iStatLuc,
                  pChar->pStats->iStatHP,
                  pChar->pStats->iStatMP,
                  pChar->pStats->iStatMV );
          }
          else if ( str_prefix( cArg, "Yes" ) == TRUE )
          {
              /*
               * Goto the character creation menu.
               */
              term_send_string( pTerm, "%s"
                "\n\r__ Character Creation Menu __\n\r"
                "\n\r"
                "  1) Choose race.                       Race: %s\n\r"
                "  2) Roll stats.                Stats rolled: %s\n\r"
                "  3) Choose sex.                         Sex: %s\n\r"
                "  4) Finish creation.\n\r"
                "\n\r"
                "Choose an option: ", make_ansi_code(
                ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
                ( pChar->iRace == 0 ? "None"
                : get_race_string( pChar->iRace ) ),
                ( pChar->pStats->iStatStr == 0 ? "No" : "Yes" ),
                ( pChar->iSex == 0 ? "None"
                : get_sex_string( pChar->iSex ) ) );
              pTerm->iConState              = CON_CREATION_MENU;
          }
          else
              term_send_string( pTerm, "\n\rYes or no? " );

          break;

      case ( CON_GET_SEX )     :
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rSex: " );
              break;
          }

          if ( str_prefix( cArg, "Male" ) == TRUE )
              pChar->iSex                   = NUMBER_SEX_MALE;
          else if ( str_prefix( cArg, "Female" ) == TRUE )
              pChar->iSex                   = NUMBER_SEX_FEMALE;
          else
          {
              term_send_string( pTerm, "\n\rThat is not a sex.\n\r" );
              term_send_string( pTerm, "\n\rSex: " );
              return;
          }
#if 0
          i = get_race_index( pChar->iRace );

/*          if ( rRaceTable[i].iHairColors[1] == 0 )
          {
              pChar->iEyeColor              = rRaceTable[i].iHairColors[0];
              write_string_buffer( pTerm, "\n\rSex (Male/Female)? " );
              pTerm->iConState              = CON_GET_EYE_COLOR;
              return;
          } */

          strcpy( cBuf,
            "\n\rThe following values have no affect on the game.\n\r" );
          strcat( cBuf,
            "\n\rThe following hair colors are available for your race:"
            "\n\r " );


          for ( i2 = 0; rRaceTable[i].iHairColors[i2]; i2++ )
          {
              for ( i3 = 0; snHairColorTable[i3].pName[0] != '\0'; i3++ )
              {
                  if ( rRaceTable[i].iHairColors[i2]
                    == snHairColorTable[i3].iNumber )
                  {
                      strcat( cBuf, " " );
                      strcat( cBuf, snHairColorTable[i3].pName );
                  }
              }
          }

          strcat( cBuf, "\n\r" );
          write_string_buffer( pTerm, cBuf );
          write_string_buffer( pTerm, "\n\rHair Color: " );
          pTerm->iConState                  = CON_GET_HAIR_COLOR;
#endif
          term_send_string( pTerm, "%s"
            "\n\r__ Character Creation Menu __\n\r"
            "\n\r"
            "  1) Choose race.                       Race: %s\n\r"
            "  2) Roll stats.                Stats rolled: %s\n\r"
            "  3) Choose sex.                         Sex: %s\n\r"
            "  4) Finish creation.\n\r"
            "\n\r"
            "Choose an option: ", make_ansi_code(
            ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ),
            ( pChar->iRace == 0 ? "None"
            : get_race_string( pChar->iRace ) ),
            ( pChar->pStats->iStatStr == 0 ? "No" : "Yes" ),
            ( pChar->iSex == 0 ? "None"
            : get_sex_string( pChar->iSex ) ) );
          pTerm->iConState                  = CON_CREATION_MENU;
          break;

      case ( CON_GET_HAIR_COLOR ):
          if ( cArg[0] == '\0' )
          {
              term_send_string( pTerm, "\n\rHair Color: " );
              break;
          }

          i = get_race_index( pChar->iRace );

          for ( i2 = 0; snHairColorTable[i2].pName[0] != '\0'; i2++ )
          {
              if ( str_prefix( cArg, snHairColorTable[i2].pName ) == TRUE )
              {
                  for ( i3 = 0; rRaceTable[i].iHairColors[i3] != 0; i3++ )
                  {
                      if ( snHairColorTable[i2].iNumber
                        == rRaceTable[i].iHairColors[i3] )
                      {

                          pChar->iHairColor = snHairColorTable[i].iNumber;
                          term_send_string( pTerm,
                            "%s\n\r%s\n\r%s[Press Enter to continue]%s ",
                            make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL,
                            NULL, pTerm ), pMOTD, make_ansi_code(
                            ANSI_CODEREF_SGR_DSET, "0;44", NULL, pTerm ),
                            make_ansi_code( ANSI_CODEREF_SGR_DSET, "0",
                            NULL, pTerm ) );
                          pTerm->iConState  = CON_READ_MOTD;
                          return;
                      }
                  }
              }
          }

          term_send_string( pTerm,
            "\n\rI do not know the hair color you are refering to.\n\r"
            "\n\rHair Color: " );
          break;

      case ( CON_READ_MOTD )   :
          if ( pChar->iLevel < 1 )
          {
              pChar->iLevel    = 1;
              pChar->iHP       = pChar->pStats->iStatHP;
              pChar->iMP       = pChar->pStats->iStatMP;
              pChar->iMV       = pChar->pStats->iStatMV;
              pChar->iPosition = NUMBER_POSITION_STANDING;
              pChar->iMaxCarry = ( pChar->pStats->iStatStr - 2 );
              pStartingRoom    = uDefaultRoom.pRoom;
          }
          else
          {
              if ( ( pChar = load_pc( pName ) ) != NULL )
              {
                  pTerm->pChar->pInRoom     = NULL;
                  free_char( &pTerm->pChar );
                  pTerm->pChar              = pChar;
                  pChar->pTerm              = pTerm;
              }
          }

          pChar->pPCData->tLastLogin        = tCurrentTime;
          pTerm->iConState                  = CON_PLAYING;
          char_to_room( pChar, pStartingRoom );
          send_game_message( "~h appears in a puff of smoke.",
            MESSAGE_DEST_ROOM, TRUE, pChar, NULL, pChar->pInRoom, TRUE,
            NUMBER_POSITION_RESTING, pChar );
          lprintf( "%s has entered the game.", pName );
          term_send_string( pTerm, "%s\n\r", make_ansi_code(
            ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ) );
          cmd_look( pChar, "" );
          break;

      case ( CON_RECONNECT ):
          {
              char *pPass = pChar->pPCData->sPassword;

              if ( pPass[0] != '\0'
                && strcmp( pPass, crypt( pArg, pPass ) ) )
              {
                  term_send_string( pTerm,
                    "\n\rI now see that you are not who you say you are "
                    "... Goodbye.\n\r" );
                  pTerm->pChar              = NULL;
                  close_connection( &pTerm );
                  return;
              }

              send_game_message( "~c's image shimers for a moment.",
                MESSAGE_DEST_ROOM, TRUE, pChar, NULL, pChar->pInRoom,
                FALSE, NUMBER_POSITION_RESTING, pChar );

              if ( pChar->pTerm == NULL )
                  term_send_string( pTerm, "%s\n\rReconnected.\n\r",
                    make_ansi_code( ANSI_CODEREF_EF_CSCREEN, NULL, NULL,
                    pTerm ) );
              else
              {
                  term_send_string( pTerm,
                    "%s\n\rOld connection broken.\n\r", make_ansi_code(
                    ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ) );
                  pChar->pTerm->pChar       = NULL;
                  term_send_string( pChar->pTerm,
                    "\n\rYour connection has been broken by a new "
                    "connection.\n\r" );
                  close_connection( &pChar->pTerm );
              }

              pChar->pTerm                  = pTerm;
              pTerm->iConState              = CON_PLAYING;
          }

          break;

#ifdef DEBUG
      default                  :
          wcdebug( "Unknown connected state: %d.  Closing connection.",
            pTerm->iConState );
          close_connection( &pTerm );
          break;
#endif
    }
}


static bool illegal_name( char *pName )
{
    int iLength;
    int i, i2;

    iLength = strlen( pName );

    if ( iLength > 12 || iLength < 2
      || sp_legal_string( pName ) == FALSE )
        return ( TRUE );

    if ( str_compare( pName, "North" ) == TRUE
      || str_compare( pName, "South" ) == TRUE
      || str_compare( pName, "East" ) == TRUE
      || str_compare( pName, "West" ) == TRUE
      || str_compare( pName, "Up" ) == TRUE
      || str_compare( pName, "Down" ) == TRUE
      || str_compare( pName, "NorthEast" ) == TRUE
      || str_compare( pName, "SouthWest" ) == TRUE
      || str_compare( pName, "NorthWest" ) == TRUE
      || str_compare( pName, "SouthEast" ) == TRUE
      || str_compare( pName, "NE" ) == TRUE
      || str_compare( pName, "SW" ) == TRUE
      || str_compare( pName, "NW" ) == TRUE
      || str_compare( pName, "SE" ) == TRUE
      || str_compare( pName, "Everyone" ) == TRUE
      || str_compare( pName, "Everybody" ) == TRUE
      || str_compare( pName, "Party" ) == TRUE
      || str_compare( pName, "Save" ) == TRUE
      || str_compare( pName, "Quit" ) == TRUE )
        return ( TRUE );

    for ( i = 0; pName[i] != '\0'; i++ )
    {
        for ( i2 = 0; cLetterTable[i2] != '\0'; i2++ )
        {
            if ( pName[i] == cLetterTable[i2] )
                break;
        }

        if ( cLetterTable[i2] == '\0' )
            return ( TRUE );
    }

    return ( FALSE );
}


static CHAR_DATA *new_guest( void )
{
    static intt iGuestNum;
    CHAR_DATA *pChar;
    char cBuf[128];

    pChar                  = new_pc( );
    snprintf( cBuf, 128, "Guest%u", (unsigned int) iGuestNum );
    pChar->pPCData->sName  = save_string( cBuf );
    pChar->pPCData->bGuest = TRUE;
    iGuestNum++;

    return ( pChar );
}


/*
 * Simplified ANSI code interface.
 */
char *make_ansi_code( int iCodeRef, char *pArg1, char *pArg2,
                      TERM_DATA *pTerm )
{
    char *pBuf = new_buffer( );

    if ( pTerm != NULL && pTerm->iTermType != CLIENT_ANSI_COMPATIBLE )
        return ( EMPTY_STRING );

    switch ( iCodeRef )
    {
      case ANSI_CODEREF_CC_SET     :
          sprintf( pBuf, "\033[%s;%sH", pArg1, pArg2 );
          break;

      case ANSI_CODEREF_CC_UP      :
          sprintf( pBuf, "\033[%sA", pArg1 );
          break;

      case ANSI_CODEREF_CC_DOWN    :
          sprintf( pBuf, "\033[%sB", pArg1 );
          break;

      case ANSI_CODEREF_CC_FORWARD :
          sprintf( pBuf, "\033[%sC", pArg1 );
          break;

      case ANSI_CODEREF_CC_BACK    :
          sprintf( pBuf, "\033[%sD", pArg1 );
          break;

      case ANSI_CODEREF_EF_CSCREEN :
          strcpy( pBuf, "\033[2J" );
          break;

      case ANSI_CODEREF_EF_CLINE   :
          strcpy( pBuf, "\033[K" );
          break;

      case ANSI_CODEREF_SGR_DSET   :
          sprintf( pBuf, "\033[%sm", pArg1 );
          break;

      case ANSI_CODEREF_SGR_SSET   :
          sprintf( pBuf, "\033[=%s;7h", pArg1 );
          break;

      case ANSI_CODEREF_SGR_SRSET  :
          sprintf( pBuf, "\033[=%s;7l", pArg1 );
          break;

      case ANSI_CODEREF_KR_REASSIGN:
          sprintf( pBuf, "\033[%s;%sp", pArg1, pArg2 );
          break;

      case ANSI_CODEREF_OTHER      :
          strcpy( pBuf, pArg1 );
          break;

      default                      : return ( EMPTY_STRING );
    }

    return ( pBuf );
}


/*
 * End of comm.c
 */