/
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 <string.h>
#include <ctype.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "sapphire.h"


/*
 * Functions
 */
void process_shell_cmd( TERM_DATA *pTerm, char *pCommand )
{
    char cCommand[MAX_INPUT];
    int i;

    if ( next_char( pCommand ) == '\0' )
        return;

    pCommand = one_arg( pCommand, cCommand );

    for ( i = 0; scShellCmdTable[i].pName[0] != '\0'; i++ )
    {
        if ( strcmp( cCommand, scShellCmdTable[i].pName ) == 0 )
        {
            ( *scShellCmdTable[i].pCmdFunc ) ( pTerm, pCommand );
            return;
        }
    }
#if 0
{
    char **argv;
    char *env[]  = { NULL };
    pid_t pPid;
    int iStdin[2], iStdout[2], iStderr[2];

    pipe( iStdin );
    pipe( iStdout );
    pipe( iStderr );

    if ( ( pPid = fork( ) ) == 0 )
    {
        dup2( iStdin[0], STDIN_FILENO );
        dup2( iStdout[1], STDOUT_FILENO );
        dup2( iStderr[1], STDERR_FILENO );

        argv    = alloc_mem( ( sizeof( char * ) * 2 ) );
        argv[0] = str_dup( cCommand );

        for ( i = 1; pCommand[0] != '\0'; i++ )
        {
            argv = realloc_mem( argv, ( sizeof( char * ) * ( i + 2 ) ) );
            pCommand = one_arg( pCommand, cCommand );
            argv[i] = str_dup( cCommand );
        }

        argv[i] = NULL;
        execve( argv[0], argv, env );
        printf( "execve() failed for `%s': %s.\n", argv[0],
          strerror( errno ) );
        exit( 1 );
    }
    else
    {
        pTerm->iConState     = CON_RUNNING_CHILD;
        pTerm->iConStateOld  = CON_SYSTEM_SHELL;
        pTerm->pChildP       = new_child( pPid, iStdin, iStdout, iStderr,
                                 cCommand );
        pTerm->pInBuffer[0]  = '\0';
        flush_buffer( pTerm );
        return;
    }
}
#endif
    write_string_buffer( pTerm, "Command not found.\n\r\n\r" );
}


/*
 * Command to enter the development shell.
 */
void cmd_shell( CHAR_DATA *pChar, char *pArgument )
{
    TERM_DATA *pTerm;
    char cBuf[256];

    if ( pChar->pTerm == NULL )
        return;

    if ( IS_NPC( pChar ) )
    {
        send_string( pChar,
          "Why would NPCs need to use the development shell?\n\r" );
        return;
    }

    send_game_message( "~c has left this world.", MESSAGE_DEST_ROOM, TRUE,
      pChar, NULL, pChar->pInRoom, TRUE, NUMBER_POSITION_RESTING, pChar );
    char_from_room( pChar );
    write_string_buffer( pChar->pTerm, make_ansi_code(
      ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pChar->pTerm ) );
    send_string( pChar,
      "\n\rType `exit' to leave the development shell.\n\r\n\r" );

    for ( pTerm = pTermList; pTerm != NULL; pTerm = pTerm->pNext )
    {
        if ( pTerm->iConState == CON_SYSTEM_SHELL )
        {
            sprintf( cBuf,
              "\n\r%s@%s has entered the development shell.\n\r\n\r",
              pChar->pPCData->sName, pChar->pTerm->pHostname );
            write_string_buffer( pTerm, cBuf );
        }
    }

    pChar->pTerm->iConState = CON_SYSTEM_SHELL;
}


/*
 * Returns TRUE is pPath is a valid path.
 */
static bool legal_path( TERM_DATA *pTerm, char *pPath )
{
    char cBuf[512];
    char *pBuf      = pTerm->pCurrentDir;
    int iCount      = 0;
    int i;

    while ( ( pBuf = strchr( pBuf, '/' ) ) != NULL )
    {
        pBuf++;
        iCount++;
    }

    while ( pPath[0] != '\0' )
    {
        if ( ( pBuf = strchr( pPath, '/' ) ) == NULL )
        {
            strcpy( cBuf, pPath );
            pPath   = EMPTY_STRING;
        }
        else
        {
            strncpy( cBuf, pPath, ( i = ( pBuf - pPath ) ) );
            cBuf[i] = '\0';
            pPath   = ( pBuf + 1 );
        }

        if ( strcmp( cBuf, ".." ) == 0 )
        {
            iCount--;

            if ( iCount < 0 )
                return ( FALSE );
        }
        else if ( cBuf[0] != '\0' && strcmp( cBuf, "." ) != 0 )
            iCount++;
    }

    return ( TRUE );
}


/*
 * Removes multiple slashes, `.' and `dir/..' from a path string.
 */
static char *clean_up_path( char *pPath )
{
    char *pOutBuf = new_buffer( );
    char *pBuf    = pPath;
    char *pBuf2;
    char cBuf[MAX_STRING];
    int iCount    = 0;

    pOutBuf[0]    = '\0';

    if ( *pBuf == '/' )
        strcpy( pOutBuf, "/" );

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

        pBuf      = edit_str( pBuf, cBuf, ' ', "/" );

        if ( strcmp( cBuf, ".." ) == 0 )
        {
            if ( iCount > 0 )
            {
                iCount--;
                pOutBuf[strlen( pOutBuf ) - 1] = '\0';

                if ( ( pBuf2 = strrchr( pOutBuf, '/' ) ) != NULL )
                    *++pBuf2                   = '\0';
                else
                {
                    if ( *pOutBuf == '/' )
                        pOutBuf[1]             = '\0';
                    else
                        *pOutBuf               = '\0';
                }

                continue;
            }
            else
                strcat( pOutBuf, ".." );
        }
        else if ( strcmp( cBuf, "." ) == 0 )
            continue;
        else
        {
            iCount++;
            strcat( pOutBuf, cBuf );
        }

        if ( *pBuf == '/' )
            strcat( pOutBuf, "/" );
    }

    return ( pOutBuf );
}


/*
 * Command to exit the development shell.
 */
void shell_cmd_exit( TERM_DATA *pTerm, char *pArgument )
{
    TERM_DATA *pTerm2;
    char cBuf[256];

    pTerm->iConState = CON_PLAYING;

    if ( pTerm->pChar != NULL )
    {
        for ( pTerm2 = pTermList; pTerm2 != NULL; pTerm2 = pTerm2->pNext )
        {
            if ( pTerm2->iConState == CON_SYSTEM_SHELL )
            {
                sprintf( cBuf,
                  "\n\r%s@%s has left the development shell.\n\r\n\r",
                  pTerm->pChar->pPCData->sName, pTerm->pHostname );
                write_string_buffer( pTerm2, cBuf );
            }
        }

        char_to_room( pTerm->pChar, uDefaultRoom.pRoom );
        send_game_message( "~h appears out of nothingness.",
          MESSAGE_DEST_ROOM, TRUE, pTerm->pChar, NULL,
          pTerm->pChar->pInRoom, TRUE, NUMBER_POSITION_RESTING,
          pTerm->pChar );
        write_string_buffer( pTerm, make_ansi_code(
          ANSI_CODEREF_EF_CSCREEN, NULL, NULL, pTerm ) );
    }
}


/*
 * Changes the user's directory.
 */
void shell_cmd_cd( TERM_DATA *pTerm, char *pArgument )
{
    DIR *pDir;
    char cBuf[1024];
    char cArg[MAX_INPUT];
    char *pArg             = cArg;

    pArgument              = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 2 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    strcpy( cBuf, pShellDir );

    if ( cArg[0] == '/' )
        pArg++;
    else
        strcat( cBuf, pTerm->pCurrentDir );

    if ( cArg[0] != '\0' )
    {
        if ( legal_path( pTerm, cArg ) != TRUE )
        {
            write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
            return;
        }

        strcat( cBuf, pArg );
    }

    if ( ( pDir = opendir( cBuf ) ) == NULL )
    {
        write_string_buffer( pTerm, "No such directory.\n\r\n\r" );
        return;
    }

    if ( readdir( pDir ) == NULL )
    {
        closedir( pDir );
        write_string_buffer( pTerm, "Not a directory.\n\r\n\r" );
        return;
    }

    closedir( pDir );

    if ( pArg[0] == '\0' )
        cBuf[0]            = '\0';
    else
    {
        if ( cArg[0] != '/' )
        {
            strcpy( cBuf, pTerm->pCurrentDir );
            strcat( cBuf, pArg );
        }
        else
            strcpy( cBuf, pArg );

        if ( cBuf[strlen( cBuf ) - 1] != '/' )
            strcat( cBuf, "/" );
    }

    str_free( pTerm->pCurrentDir );
    pTerm->pCurrentDir     = str_dup( clean_up_path( cBuf ) );
}


/*
 * Removes (deletes) a file.
 */
void shell_cmd_rm( TERM_DATA *pTerm, char *pArgument )
{
    DIR *pDir;
    FILE *pFile;
    char cBuf[1024];
    char cArg[MAX_INPUT];

    pArgument              = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( cArg[0] == '\0' )
    {
        write_string_buffer( pTerm, "Usage: rm <file>\n\r\n\r" );
        return;
    }

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 1 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    if ( legal_path( pTerm, cArg ) != TRUE )
    {
        write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
        return;
    }

    strcpy( cBuf, pShellDir );
    strcat( cBuf, pTerm->pCurrentDir );
    strcat( cBuf, cArg );

    if ( ( pDir = opendir( cBuf ) ) != NULL )
    {
        if ( readdir( pDir ) != NULL )
        {
            closedir( pDir );
            write_string_buffer( pTerm,
              "File is a directory.\n\r\n\r" );
            return;
        }

        closedir( pDir );
    }

    if ( ( pFile = fopen( cBuf, "r" ) ) == NULL )
    {
        write_string_buffer( pTerm, "No such file.\n\r\n\r" );
        return;
    }

    fclose( pFile );
    unlink( cBuf );
}


/*
 * Creates a directory.
 */
void shell_cmd_mkdir( TERM_DATA *pTerm, char *pArgument )
{
    char cBuf[1024];
    char cArg[MAX_INPUT];

    pArgument              = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( cArg[0] == '\0' )
    {
        write_string_buffer( pTerm,
          "Usage: mkdir <directory name>\n\r\n\r" );
        return;
    }

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 1 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    if ( legal_path( pTerm, cArg ) != TRUE )
    {
        write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
        return;
    }

    strcpy( cBuf, pShellDir );
    strcat( cBuf, pTerm->pCurrentDir );
    strcat( cBuf, cArg );

    mkdir( cBuf, ( S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ) );
}


/*
 * Removes a directory.
 */
void shell_cmd_rmdir( TERM_DATA *pTerm, char *pArgument )
{
    DIR *pDir;
    char cBuf[1024];
    char cArg[MAX_INPUT];

    pArgument              = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( cArg[0] == '\0' )
    {
        write_string_buffer( pTerm,
          "Usage: rmdir <directory>\n\r\n\r" );
        return;
    }

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 1 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    if ( legal_path( pTerm, cArg ) != TRUE )
    {
        write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
        return;
    }

    strcpy( cBuf, pShellDir );
    strcat( cBuf, pTerm->pCurrentDir );
    strcat( cBuf, cArg );

    if ( ( pDir = opendir( cBuf ) ) == NULL )
    {
        write_string_buffer( pTerm, "No such directory.\n\r\n\r" );
        return;
    }

    if ( readdir( pDir ) == NULL )
    {
        closedir( pDir );
        write_string_buffer( pTerm, "Not a directory.\n\r\n\r" );
        return;
    }

    closedir( pDir );

    if ( rmdir( cBuf ) == ENOTEMPTY )
        write_string_buffer( pTerm, "Directory not empty.\n\r\n\r" );
}


/*
 * Shows a directory listing.
 */
void shell_cmd_ls( TERM_DATA *pTerm, char *pArgument )
{
    struct dirent *pDirEntry;
    DIR *pDir;
    char cBuf[1024];
    char cArg[MAX_INPUT];
    int iCount     = 0;

    pArgument      = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 2 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    strcpy( cBuf, pShellDir );
    strcat( cBuf, pTerm->pCurrentDir );

    if ( cArg[0] != '\0' )
    {
        if ( legal_path( pTerm, cArg ) != TRUE )
        {
            write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
            return;
        }

        strcat( cBuf, cArg );
    }
    else
        strcat( cBuf, "." );

    if ( ( pDir = opendir( cBuf ) ) == NULL )
    {
        write_string_buffer( pTerm, "No such directory.\n\r\n\r" );
        return;
    }

    if ( readdir( pDir ) == NULL )
    {
        closedir( pDir );
        write_string_buffer( pTerm, "Not a directory.\n\r\n\r" );
        return;
    }

    readdir( pDir );

    setup_string_pager( pTerm );

    while ( ( pDirEntry = readdir( pDir ) ) != NULL )
    {
        sprintf( cBuf, "%-32.32s ", pDirEntry->d_name );
        page_string( pTerm, cBuf );
        iCount++;

        if ( iCount == 2 )
        {
            page_string( pTerm, "\n\r" );
            iCount = 0;
        }
    }

    if ( iCount > 0 )
        page_string( pTerm, "\n\r" );

    page_string( pTerm, "\n\r" );
    finish_string_pager( pTerm );
    closedir( pDir );
}


/*
 * Displays a file to the user.
 */
void shell_cmd_cat( TERM_DATA *pTerm, char *pArgument )
{
    DIR *pDir;
    FILE *pFile;
    char cBuf[1024];
    char cArg[MAX_INPUT];
    int i;

    pArgument       = one_arg( pArgument, cArg );
    strcpy( cArg, clean_up_path( cArg ) );

    if ( cArg[0] == '\0' )
    {
        write_string_buffer( pTerm, "Usage: cat <input file>\n\r\n\r" );
        return;
    }

    if ( ( strlen( pShellDir ) + strlen( pTerm->pCurrentDir )
      + strlen( cArg ) + 1 ) > 1024 )
    {
        write_string_buffer( pTerm, "Path too long.\n\r\n\r" );
        str_free( pTerm->pCurrentDir );
        pTerm->pCurrentDir = EMPTY_STRING;
        return;
    }

    if ( legal_path( pTerm, cArg ) != TRUE )
    {
        write_string_buffer( pTerm, "Illegal path.\n\r\n\r" );
        return;
    }

    strcpy( cBuf, pShellDir );
    strcat( cBuf, pTerm->pCurrentDir );
    strcat( cBuf, cArg );

    if ( ( pDir = opendir( cBuf ) ) != NULL )
    {
        if ( readdir( pDir ) != NULL )
        {
            closedir( pDir );
            write_string_buffer( pTerm,
              "File is a directory.\n\r\n\r" );
            return;
        }

        closedir( pDir );
    }

    if ( ( pFile = fopen( cBuf, "r" ) ) == NULL )
    {
        write_string_buffer( pTerm, "No such file.\n\r\n\r" );
        return;
    }

    setup_string_pager( pTerm );

    for ( i = 0; feof( pFile ) == 0; i++ )
    {
        if ( i == 256 )
        {
            cBuf[i] = '\0';
            page_string( pTerm, cBuf );
            i       = 0;
        }

        cBuf[i]     = getc( pFile );
    }

    cBuf[--i]       = '\0';

    if ( i > 0 )
        page_string( pTerm, cBuf );

    page_string( pTerm, "\n\r\n\r" );
    finish_string_pager( pTerm );
    fclose( pFile );
}


/*
 * Shows a list of everyone logged onto the server.
 */
void shell_cmd_users( TERM_DATA *pTerm, char *pArgument )
{
    TERM_DATA *pTerm2;
    char cBuf[128];

    setup_string_pager( pTerm );

    for ( pTerm2 = pTermList; pTerm2 != NULL; pTerm2 = pTerm2->pNext )
    {
        sprintf( cBuf, "[%3d %3d] [%s] : %-50.50s\n\r",
          pTerm2->sTelnetSocket, pTerm2->sBinarySocket,
          state_string( pTerm2->iConState ), pTerm2->pHostname );
        page_string( pTerm, cBuf );
    }

    page_string( pTerm, "\n\r" );
    finish_string_pager( pTerm );
}


/*
 * Shows a list of everyone in the development shell.
 */
void shell_cmd_who( TERM_DATA *pTerm, char *pArgument )
{
    TERM_DATA *pTerm2;
    char cBuf[128];

    setup_string_pager( pTerm );

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

    page_string( pTerm, "\n\r" );
    finish_string_pager( pTerm );
}


/*
 * Sends a message to another user in the development shell.
 */
void shell_cmd_tell( TERM_DATA *pTerm, char *pArgument )
{
    TERM_DATA *pToTerm;
    char cArg[MAX_INPUT];
    char cBuf[MAX_STRING];

    pArgument = one_arg( pArgument, cArg );

    if ( cArg[0] == '\0' || pArgument[0] == '\0' )
    {
        write_string_buffer( pTerm,
          "Usage: tell <name> <message>\n\r\n\r" );
        return;
    }

    for ( pToTerm = pTermList; pToTerm != NULL;
      pToTerm = pToTerm->pNext )
    {
        if ( pToTerm->iConState == CON_SYSTEM_SHELL
          && pToTerm->pChar != NULL
          && multi_compare( cArg, pToTerm->pChar->pPCData->sName )
          == TRUE )
            break;
    }

    if ( pToTerm == NULL )
    {
        write_string_buffer( pTerm, "Party not found.\n\r\n\r" );
        return;
    }

    if ( pToTerm == pTerm )
    {
        write_string_buffer( pTerm,
          "Must be someone besides yourself.\n\r\n\r" );
        return;
    }

    if ( pTerm->pChar != NULL )
        snprintf( cBuf, MAX_STRING, "\n\r%s tells you: %s\n\r\n\r",
          pTerm->pChar->pPCData->sName, pArgument );
    else
        snprintf( cBuf, MAX_STRING, "\n\rSomeone tells you: %s\n\r\n\r",
          pTerm->pChar->pPCData->sName, pArgument );

    write_string_buffer( pToTerm, cBuf );
    write_string_buffer( pTerm, "Message sent.\n\r\n\r" );
}


/*
 * End of shell.c
 */