RaM Fire Updated/
/*
 * RAM $Id: olc_utils.c 81 2009-01-14 06:16:31Z ghasatta $
 */

/***************************************************************************
 *  File: string.c                                                         *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 *                                                                         *
 *  This code was freely distributed with the The Isles 1.1 source code,   *
 *  and has been used here for OLC - OLC would not be what it is without   *
 *  all the previous coders who released their source code.                *
 *                                                                         *
 ***************************************************************************/

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "merc.h"
#include "db.h"
#include "interp.h"
#include "tables.h"
#include "strings.h"
#include "olc.h"

/*****************************************************************************
 Name:           string_edit
 Purpose:        Clears string and puts player into editing mode.
 Called by:      none
 ****************************************************************************/
void string_edit( CHAR_DATA *ch, char **pString )
{
    ch_printf( ch, "-========- Entering EDIT Mode -=========-\r\n" );
    ch_printf( ch, "    Type .h on a new line for help\r\n" );
    ch_printf( ch, " Terminate with a ~ or @ on a blank line.\r\n" );
    ch_printf( ch, "-=======================================-\r\n" );

    if ( *pString == NULL )
    {
        *pString = str_dup( "" );
    }
    else
    {
        **pString = '\0';
    }

    ch->desc->pString = pString;

    return;
}

/*****************************************************************************
 Name:           string_append
 Purpose:        Puts player into append mode for given string.
 Called by:      (many)olc_act.c
 ****************************************************************************/
void string_append( CHAR_DATA *ch, std::string &pString )
{
    ch_printf( ch, "-=======- Entering APPEND Mode -========-\r\n" );
    ch_printf( ch, "    Type .h on a new line for help\r\n" );
    ch_printf( ch, " Terminate with a ~ or @ on a blank line.\r\n" );
    ch_printf( ch, "-=======================================-\r\n" );

    ch_printf( ch, "%s", numlineas( pString.c_str() ) );

    ch->desc->pString_std = &pString;
    ch->desc->pString = NULL;
}

void string_append( CHAR_DATA *ch, char **pString )
{
    ch_printf( ch, "-=======- Entering APPEND Mode -========-\r\n" );
    ch_printf( ch, "    Type .h on a new line for help\r\n" );
    ch_printf( ch, " Terminate with a ~ or @ on a blank line.\r\n" );
    ch_printf( ch, "-=======================================-\r\n" );   

    ch_printf( ch, "%s", numlineas( *pString ) );

    ch->desc->pString_std = NULL;
    ch->desc->pString = pString;
}

/*****************************************************************************
 Name:           string_replace
 Purpose:        Substitutes one string for another.
 Called by:      string_add(string.c) (aedit_builder)olc_act.c.
 ****************************************************************************/
char                   *string_replace( char *orig, const char *pOld, const char *pNew )
{
    char                    xbuf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                     i = 0;

    strcpy( xbuf, orig );
    if ( strstr( orig, pOld ) != NULL )
    {
        i = strlen( orig ) - strlen( strstr( orig, pOld ) );
        xbuf[i] = '\0';
        strcat( xbuf, pNew );
        strcat( xbuf, &orig[i + strlen( pOld )] );
        free_string( orig );
    }

    return str_dup( xbuf );
}

void                   string_replace(std::string &orig, const char *pOld, const char *pNew)
{
    size_t old_size = strlen(pOld);

    size_t replace_pos = orig.find(pOld);

    if(replace_pos != std::string::npos)
	orig.replace(replace_pos, old_size, pNew);
}

/*****************************************************************************
 Name:           string_add
 Purpose:        Interpreter for string editing.
 Called by:      game_loop_xxxx(comm.c).
 ****************************************************************************/
void string_add( CHAR_DATA *ch, const char *argument )
{
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                    local_argument[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
    const char             *lap = local_argument;

    /*
     * Thanks to James Seng
     */
    strcpy( local_argument, argument );
    smash_tilde( local_argument );

    if ( *lap == '.' )
    {
        char                    arg1[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
        char                    arg2[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
        char                    arg3[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";
        char                    tmparg3[MAX_INPUT_LENGTH] = "\0\0\0\0\0\0\0";

        lap = one_argument( lap, arg1 );
        lap = first_arg( lap, arg2, FALSE );
        strcpy( tmparg3, lap );
        lap = first_arg( lap, arg3, FALSE );

        if ( !str_cmp( arg1, ".c" ) )
        {
            ch_printf( ch, "String cleared.\r\n" );

	    if(ch->desc->pString != NULL)
	    {
		free_string( *ch->desc->pString );
		*ch->desc->pString = str_dup( "" );
	    }

	    else
		ch->desc->pString_std->erase();
	    
            return;
        }

        if ( !str_cmp( arg1, ".s" ) )
        {
	    const char *print_me = NULL;

	    if(ch->desc->pString != NULL)
		print_me = *ch->desc->pString;
	    else
		print_me = ch->desc->pString_std->c_str();
	    
            ch_printf( ch, "String so far:\r\n" );

            ch_printf( ch, "%s", numlineas( print_me ) );
            return;
        }

        if ( !str_cmp( arg1, ".r" ) )
        {
            if ( arg2[0] == '\0' )
            {
                ch_printf( ch, "usage:  .r \"old string\" \"new string\"\r\n" );
                return;
            }

	    if(ch->desc->pString != NULL)
		*ch->desc->pString = string_replace( *ch->desc->pString, arg2, arg3 );
	    else
		string_replace(*ch->desc->pString_std, arg2, arg3);
	    
            ch_printf( ch, "'%s' replaced with '%s'.\r\n", arg2, arg3 );
            return;
        }

        if ( !str_cmp( arg1, ".f" ) )
        {
	    if(ch->desc->pString != NULL)
		*ch->desc->pString = format_string( *ch->desc->pString );
	    else
		format_string(*ch->desc->pString_std);
	    
            ch_printf( ch, "String formatted.\r\n" );
            return;
        }

        if ( !str_cmp( arg1, ".ld" ) )
        {
	    if(ch->desc->pString != NULL)
		*ch->desc->pString = string_linedel( *ch->desc->pString, atoi( arg2 ) );
	    else
		string_linedel(*ch->desc->pString_std, atoi(arg2));

	    ch_printf( ch, "Line deleted.\r\n" );
            return;
        }

        if ( !str_cmp( arg1, ".li" ) )
        {
            if(ch->desc->pString != NULL)
		*ch->desc->pString =
		    string_lineadd( *ch->desc->pString, tmparg3, atoi( arg2 ) );
	    else
		string_lineadd(*ch->desc->pString, tmparg3, atoi(arg2));
	    
            ch_printf( ch, "Line inserted.\r\n" );
            return;
        }

        if ( !str_cmp( arg1, ".lr" ) )
        {
	    if(ch->desc->pString != NULL)
	    {
		*ch->desc->pString = string_linedel( *ch->desc->pString, atoi( arg2 ) );
		*ch->desc->pString =
		    string_lineadd( *ch->desc->pString, tmparg3, atoi( arg2 ) );
	    }

	    else
	    {
		string_linedel(*ch->desc->pString_std, atoi(arg2));
		string_lineadd(*ch->desc->pString_std, tmparg3, atoi(arg2));
	    }
	    
            ch_printf( ch, "Line replaced.\r\n" );
            return;
        }

        if ( !str_cmp( arg1, ".h" ) )
        {
            ch_printf( ch, "Sedit help (commands on blank line):   \r\n" );
            ch_printf( ch, ".r 'old' 'new'   - replace a substring \r\n" );
            ch_printf( ch, "                   (requires '', \"\") \r\n" );
            ch_printf( ch, ".h               - get help (this info)\r\n" );
            ch_printf( ch, ".s               - show string so far  \r\n" );
            ch_printf( ch, ".f               - (word wrap) string  \r\n" );
            ch_printf( ch, ".c               - clear string so far \r\n" );
            ch_printf( ch, ".ld <num>        - delete line number <num>\r\n" );
            ch_printf( ch, ".li <num> <str>  - insert <str> at line <num>\r\n" );
            ch_printf( ch, ".lr <num> <str>  - replace line <num> with <str>\r\n" );
            ch_printf( ch, "@                - end string          \r\n" );
            return;
        }

        ch_printf( ch, "SEdit:  Invalid dot command.\r\n" );
        return;
    }

    if ( *argument == '~' || *argument == '@' )
    {
        if ( ch->desc->editor == ED_MPCODE )           /* for mobprogs */
        {
            MOB_INDEX_DATA         *mob = NULL;
            int                     hash = 0;
            MPROG_LIST             *mpl = NULL;
            MPROG_CODE             *mpc = NULL;

            EDIT_MPCODE( ch, mpc );

            if ( mpc != NULL )
                for ( hash = 0; hash < MAX_KEY_HASH; hash++ )
                    for ( mob = mob_index_hash[hash]; mob; mob = mob->next )
                        for ( mpl = mob->mprogs; mpl; mpl = mpl->next )
                            if ( mpl->vnum == mpc->vnum )
                            {
                                ch_printf( ch, "Fixing mob %d.\r\n", mob->vnum );
                                mpl->code = mpc->code;
                            }
        }

        ch->desc->pString = NULL;
	ch->desc->pString_std = NULL;
        return;
    }
    

    /*
     * Truncate strings to MAX_STRING_LENGTH.
     * --------------------------------------
     */
    if( (ch->desc->pString != NULL && strlen(*ch->desc->pString) + strlen(lap) >= (MAX_STRING_LENGTH - 4)) ||
	(ch->desc->pString_std != NULL && ch->desc->pString_std->size() + strlen(lap) >= (MAX_STRING_LENGTH - 4)) )
	//if ( strlen( buf ) + strlen( lap ) >= ( MAX_STRING_LENGTH - 4 ) )
    {
        ch_printf( ch, "String too long, last line skipped.\r\n" );

        /*
         * Force character out of editing mode. 
         */
        ch->desc->pString = NULL;
        return;
    }

    else if(ch->desc->pString != NULL)
	strcpy( buf, *ch->desc->pString );
    else
	strcpy(buf, ch->desc->pString_std->c_str());
    
    /*
     * Ensure no tilde's inside string.
     * --------------------------------
     */
    smash_tilde( local_argument );
    smash_tilde( buf );

    strcat( buf, lap );
    strcat( buf, "\r\n" );

    if(ch->desc->pString != NULL)
    {
	free_string( *ch->desc->pString );
	*ch->desc->pString = str_dup( buf );
    }

    else
	*ch->desc->pString_std = buf;
    
    return;
}

/*
 * Thanks to Kalgen for the new procedure (no more bug!)
 * Original wordwrap() written by Surreality.
 */
/*****************************************************************************
 Name:           format_string_const
 Purpose:        Special string formating and word-wrapping.
 Called by:      string_add(string.c) (many)olc_act.c
 ****************************************************************************/
char                   *format_string_const( const char *oldstring /* , bool fSpace */  )
{
    char                    xbuf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                    xbuf2[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                   *rdesc = NULL;
    int                     i = 0;
    bool                    cap = true;

    for ( rdesc = str_dup(oldstring); *rdesc; rdesc++ )
    {
        if ( *rdesc == '\n' )
        {
            if ( xbuf[i - 1] != ' ' )
            {
                xbuf[i] = ' ';
                i++;
            }
        }
        else if ( *rdesc == '\r' );
        else if ( *rdesc == ' ' )
        {
            if ( xbuf[i - 1] != ' ' )
            {
                xbuf[i] = ' ';
                i++;
            }
        }
        else if ( *rdesc == ')' )
        {
            if ( xbuf[i - 1] == ' ' && xbuf[i - 2] == ' ' &&
                 ( xbuf[i - 3] == '.' || xbuf[i - 3] == '?' || xbuf[i - 3] == '!' ) )
            {
                xbuf[i - 2] = *rdesc;
                xbuf[i - 1] = ' ';
                xbuf[i] = ' ';
                i++;
            }
            else
            {
                xbuf[i] = *rdesc;
                i++;
            }
        }
        else if ( *rdesc == '.' || *rdesc == '?' || *rdesc == '!' )
        {
            if ( xbuf[i - 1] == ' ' && xbuf[i - 2] == ' ' &&
                 ( xbuf[i - 3] == '.' || xbuf[i - 3] == '?' || xbuf[i - 3] == '!' ) )
            {
                xbuf[i - 2] = *rdesc;
                if ( *( rdesc + 1 ) != '\"' )
                {
                    xbuf[i - 1] = ' ';
                    xbuf[i] = ' ';
                    i++;
                }
                else
                {
                    xbuf[i - 1] = '\"';
                    xbuf[i] = ' ';
                    xbuf[i + 1] = ' ';
                    i += 2;
                    rdesc++;
                }
            }
            else
            {
                xbuf[i] = *rdesc;
                if ( *( rdesc + 1 ) != '\"' )
                {
                    xbuf[i + 1] = ' ';
                    xbuf[i + 2] = ' ';
                    i += 3;
                }
                else
                {
                    xbuf[i + 1] = '\"';
                    xbuf[i + 2] = ' ';
                    xbuf[i + 3] = ' ';
                    i += 4;
                    rdesc++;
                }
            }
            cap = true;
        }
        else
        {
            xbuf[i] = *rdesc;
            if ( cap )
            {
                cap = false;
                xbuf[i] = UPPER( xbuf[i] );
            }
            i++;
        }
    }
    xbuf[i] = 0;
    strcpy( xbuf2, xbuf );

    rdesc = xbuf2;

    xbuf[0] = 0;

    for ( ;; )
    {
        for ( i = 0; i < 77; i++ )
        {
            if ( !*( rdesc + i ) )
                break;
        }
        if ( i < 77 )
        {
            break;
        }
        for ( i = ( xbuf[0] ? 76 : 73 ); i; i-- )
        {
            if ( *( rdesc + i ) == ' ' )
                break;
        }
        if ( i )
        {
            *( rdesc + i ) = 0;
            strcat( xbuf, rdesc );
            strcat( xbuf, "\r\n" );
            rdesc += i + 1;
            while ( *rdesc == ' ' )
                rdesc++;
        }
        else
        {
            log_error( "No spaces" );
            *( rdesc + 75 ) = 0;
            strcat( xbuf, rdesc );
            strcat( xbuf, "-\r\n" );
            rdesc += 76;
        }
    }
    while ( *( rdesc + i ) && ( *( rdesc + i ) == ' ' ||
                                *( rdesc + i ) == '\n' || *( rdesc + i ) == '\r' ) )
        i--;
    *( rdesc + i + 1 ) = 0;
    strcat( xbuf, rdesc );
    if ( xbuf[strlen( xbuf ) - 2] != '\n' )
        strcat( xbuf, "\r\n" );

    free_string( rdesc );
    return ( str_dup( xbuf ) );
}

char *format_string(char *oldstring)
{
    char *retval = format_string_const(oldstring);

    free_string(oldstring);

    return retval;
}

void format_string(std::string &str)
{
    // cheating :D format_string looks scary and will need to be cleaned up in the future.
    char *newstr = format_string_const(str.c_str());

    str = newstr;

    free_string(newstr);
}

/*
 * Used above in string_add.  Because this function does not
 * modify case if fCase is FALSE and because it understands
 * parenthesis, it would probably make a nice replacement
 * for one_argument.
 */
/*****************************************************************************
 Name:           first_arg
 Purpose:        Pick off one argument from a string and return the rest.
                 Understands quates, parenthesis (barring ) ('s) and
                 percentages.
 Called by:      string_add(string.c)
 ****************************************************************************/
const char             *first_arg( const char *argument, char *arg_first, bool fCase )
{
    char                    cEnd = '\0';

    while ( *argument == ' ' )
        argument++;

    cEnd = ' ';
    if ( *argument == '\'' || *argument == '"' || *argument == '%' || *argument == '(' )
    {
        if ( *argument == '(' )
        {
            cEnd = ')';
            argument++;
        }
        else
            cEnd = *argument++;
    }

    while ( *argument != '\0' )
    {
        if ( *argument == cEnd )
        {
            argument++;
            break;
        }
        if ( fCase )
            *arg_first = LOWER( *argument );
        else
            *arg_first = *argument;
        arg_first++;
        argument++;
    }
    *arg_first = '\0';

    while ( *argument == ' ' )
        argument++;

    return argument;
}

/*
 * Used in olc_act.c for aedit_builders.
 */
char                   *string_unpad( char *argument )
{
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                   *s = argument;

    while ( *s == ' ' )
        s++;

    strcpy( buf, s );
    s = buf;

    if ( *s != '\0' )
    {
        while ( *s != '\0' )
            s++;
        s--;

        while ( *s == ' ' )
            s--;
        s++;
        *s = '\0';
    }

    free_string( argument );
    return str_dup( buf );
}

/*
 * Same as capitalize but changes the pointer's data.
 * Used in olc_act.c in aedit_builder.
 */
char                   *string_proper( char *argument )
{
    char                   *s = argument;

    while ( *s != '\0' )
    {
        if ( *s != ' ' )
        {
            *s = UPPER( *s );
            while ( *s != ' ' && *s != '\0' )
                s++;
        }
        else
        {
            s++;
        }
    }

    return argument;
}

char                   *string_linedel( char *string, int line )
{
    char                   *strtmp = string;
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                     cnt = 1;
    int                     tmp = 0;

    for ( ; *strtmp != '\0'; strtmp++ )
    {
        if ( cnt != line )
            buf[tmp++] = *strtmp;

        if ( *strtmp == '\n' )
        {
            if ( *( strtmp + 1 ) == '\r' )
            {
                if ( cnt != line )
                    buf[tmp++] = *( ++strtmp );
                else
                    ++strtmp;
            }

            cnt++;
        }
    }

    buf[tmp] = '\0';

    free_string( string );
    return str_dup( buf );
}

void string_linedel(std::string &str, int line)
{
    size_t line_begin = string_getline(str, line);
    size_t next_line_begin = string_getline(str, line + 1);

    size_t line_length;

    if(next_line_begin == std::string::npos)
	line_length = std::string::npos;
    else
	line_length = next_line_begin - line_begin;

    str.erase(line_begin, line_length);
}

size_t string_getline(const std::string &str, int line)
{
    size_t retval = 0;

    while(line-- > 1 && retval != std::string::npos)
	retval = str.find("\n", retval);

    // advance pos one more if the newline is \n\r.
    if(retval != std::string::npos && str.at(retval+1) == '\r')
	retval += 1;

    if(retval != 0 && retval != std::string::npos)
	// advance pos to point to the first character of the line.
	retval += 1;
    
    return retval;
}
    
char                   *string_lineadd( char *string, char *newstr, int line )
{
    char                   *strtmp = string;
    int                     cnt = 1;
    int                     tmp = 0;
    bool                    done = false;
    char                    buf[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";

    for ( ; *strtmp != '\0' || ( !done && cnt == line ); strtmp++ )
    {
        if ( cnt == line && !done )
        {
            strcat( buf, newstr );
            strcat( buf, "\r\n" );
            tmp += strlen( newstr ) + 2;
            cnt++;
            done = true;
        }

        buf[tmp++] = *strtmp;

        if ( done && *strtmp == '\0' )
            break;

        if ( *strtmp == '\n' )
        {
            if ( *( strtmp + 1 ) == '\r' )
                buf[tmp++] = *( ++strtmp );

            cnt++;
        }

        buf[tmp] = '\0';
    }

    free_string( string );
    return str_dup( buf );
}

// Inserts newstr + a newline as line #.
void string_lineadd(std::string &str, char *newstr, int line)
{
    size_t insertpoint = string_getline(str, line);
    std::string insertme(newstr);

    insertme += "\r\n";
    
    // if we didn't find the line, add the text to the end of the string.
    if(insertpoint == std::string::npos)
	str += insertme;
    else
	str.insert(insertpoint, insertme);
}

/* buf queda con la linea sin \r\n */
const char                   *getline( const char *str, char *buf )
{
    int                     tmp = 0;
    bool                    found = false;

    while ( *str )
    {
        if ( *str == '\n' )
        {
            found = true;
            break;
        }

        buf[tmp++] = *( str++ );
    }

    if ( found )
    {
        if ( *( str + 1 ) == '\r' )
            str += 2;
        else
            str += 1;
    }                                                  /* para que quedemos en el inicio
                                                        * de la prox linea */

    buf[tmp] = '\0';

    return str;
}

char                   *numlineas(const char *string )
{
    int                     cnt = 1;
    static char             buf[MAX_STRING_LENGTH * 2] = "\0\0\0\0\0\0\0";
    char                    buf2[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    char                    tmpb[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";

    buf[0] = '\0';                                     /* keep me, static */

    while ( *string )
    {
        string = getline( string, tmpb );
        sprintf( buf2, "%2d. %s\r\n", cnt++, tmpb );
        strcat( buf, buf2 );
    }

    return buf;
}