RaM Fire Updated/
/*
 * RAM $Id: strings.c 47 2008-11-07 22:50:47Z quixadhal $
 */

/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  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.                                                  *
 ***************************************************************************/

/***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                         *
*       ROM has been brought to you by the ROM consortium                  *
*           Russ Taylor (rtaylor@hypercube.org)                            *
*           Gabrielle Taylor (gtaylor@hypercube.org)                       *
*           Brian Moore (zump@rom.org)                                     *
*       By using this code, you have agreed to follow the terms of the     *
*       ROM license, in the file Rom24/doc/rom.license                     *
***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "merc.h"
#include "strings.h"

void                   *rgFreeList[MAX_MEM_LIST];
const size_t            rgSizeList[MAX_MEM_LIST] = {
    16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384, 32768 - 64
};

int                     nAllocString = 0;
size_t                  sAllocString = 0;
int                     nAllocPerm = 0;
size_t                  sAllocPerm = 0;

char                   *string_space = NULL;
char                   *top_string = NULL;
char                    str_empty[1];

BUFFER                 *buf_free = NULL;

/* buffer sizes */
const int               buf_size[MAX_BUF_LIST] = {
    16, 32, 64, 128, 256, 1024, 2048, 4096, 8192, 16384
};

void *alloc_mem( size_t sMem )
{
    void                   *pMem = NULL;
    size_t                 *magic = NULL;
    int                     iList = 0;

    sMem += sizeof( *magic );

    for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
    {
        if ( sMem <= rgSizeList[iList] )
            break;
    }

    if ( iList == MAX_MEM_LIST )
    {
        proper_exit( MUD_HALT, "Alloc_mem: size %zd too large.", sMem );
    }

    if ( rgFreeList[iList] == NULL )
    {
        pMem = alloc_perm( rgSizeList[iList] );
    }
    else
    {
        pMem = rgFreeList[iList];
        rgFreeList[iList] = *( ( void ** ) rgFreeList[iList] );
    }

    magic = ( size_t * ) pMem;
    *magic = MAGIC_NUM;
    pMem = ( void * ) ( ( size_t ) pMem + ( size_t ) ( sizeof( *magic ) ) );

    return pMem;
}

/*
 * Free some memory.
 * Recycle it back onto the free list for blocks of that size.
 */
void free_mem( void *pMem, size_t sMem )
{
    int                     iList = 0;
    size_t                 *magic = NULL;

    pMem = ( void * ) ( ( size_t ) pMem - ( size_t ) sizeof( *magic ) );
    magic = ( size_t * ) pMem;

    if ( *magic != MAGIC_NUM )
    {
        log_error( "Attempt to recyle invalid memory of size %zd \"%s\"", sMem,
                   ( ( char * ) pMem + sizeof( *magic ) ) );
        return;
    }

    *magic = 0;
    sMem += sizeof( *magic );

    for ( iList = 0; iList < MAX_MEM_LIST; iList++ )
    {
        if ( sMem <= rgSizeList[iList] )
            break;
    }

    if ( iList == MAX_MEM_LIST )
    {
        proper_exit( MUD_HALT, "Free_mem: size %zd too large.", sMem );
    }

    *( ( void ** ) pMem ) = rgFreeList[iList];
    rgFreeList[iList] = pMem;

    return;
}

/*
 * Allocate some permanent memory.
 * Permanent memory is never freed,
 *   pointers into it may be copied safely.
 */
void *alloc_perm( size_t sMem )
{
    static char            *pMemPerm = NULL;
    static size_t           iMemPerm = 0;
    void                   *pMem = NULL;

    while ( sMem % sizeof( int ) != 0 )
        sMem++;
    if ( sMem > MAX_PERM_BLOCK )
    {
        proper_exit( MUD_HALT, "Alloc_perm: %zd too large.", sMem );
    }

    if ( pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK )
    {
        iMemPerm = 0;
        if ( ( pMemPerm = ( char * ) calloc( 1, MAX_PERM_BLOCK ) ) == NULL )
        {
            char                   *e = strerror( errno );

            proper_exit( MUD_HALT, "Alloc_perm: %s\n", e );
        }
    }

    pMem = pMemPerm + iMemPerm;
    iMemPerm += sMem;
    nAllocPerm += 1;
    sAllocPerm += sMem;
    return pMem;
}

/*
 * Duplicate a string into dynamic memory.
 * Fread_strings are read-only and shared.
 */
char *str_dup( const char *str )
{
    char                   *str_new = NULL;

    if ( str[0] == '\0' )
        return &str_empty[0];

    if ( str >= string_space && str < top_string )
    {
        /*
         * return str; 
         */
        return ( char * ) ( size_t ) str;
        /*
         * I can't believe I just did that cast... EVIL! 
         */
    }

    str_new = ( char * ) alloc_mem( strlen( str ) + 1 );
    strcpy( str_new, str );
    return str_new;
}

/*
 * Free a string.
 * Null is legal here to simplify callers.
 * Read-only shared strings are not touched.
 */
void free_string( char *pstr )
{
    if ( pstr == NULL
         || pstr == &str_empty[0] || ( pstr >= string_space && pstr < top_string ) )
        return;

    free_mem( pstr, strlen( pstr ) + 1 );
    return;
}

/*
 * Compare strings, case insensitive.
 * Return true if different
 *   (compatibility with historical functions).
 */
bool str_cmp( const char *astr, const char *bstr )
{
    if ( astr == NULL )
    {
        log_error( "%s", "NULL left side of str_cmp" );
        return true;
    }

    if ( bstr == NULL )
    {
        log_error( "%s", "NULL right side of str_cmp" );
        return true;
    }

    for ( ; *astr || *bstr; astr++, bstr++ )
    {
        if ( LOWER( *astr ) != LOWER( *bstr ) )
            return true;
    }

    return false;
}

/*
 * Compare strings, case insensitive, for prefix matching.
 * Return true if astr not a prefix of bstr
 *   (compatibility with historical functions).
 */
bool str_prefix( const char *astr, const char *bstr )
{
    if ( astr == NULL )
    {
        log_error( "%s", "NULL left side of str_cmp" );
        return true;
    }

    if ( bstr == NULL )
    {
        log_error( "%s", "NULL right side of str_cmp" );
        return true;
    }

    for ( ; *astr; astr++, bstr++ )
    {
        if ( LOWER( *astr ) != LOWER( *bstr ) )
            return true;
    }

    return false;
}

/*
 * Compare strings, case insensitive, for match anywhere.
 * Returns true is astr not part of bstr.
 *   (compatibility with historical functions).
 */
bool str_infix( const char *astr, const char *bstr )
{
    int                     sstr1 = 0;
    int                     sstr2 = 0;
    int                     ichar = 0;
    char                    c0 = '\0';

    if ( ( c0 = LOWER( astr[0] ) ) == '\0' )
        return false;

    sstr1 = strlen( astr );
    sstr2 = strlen( bstr );

    for ( ichar = 0; ichar <= sstr2 - sstr1; ichar++ )
    {
        if ( c0 == LOWER( bstr[ichar] ) && !str_prefix( astr, bstr + ichar ) )
            return false;
    }

    return true;
}

/*
 * Compare strings, case insensitive, for suffix matching.
 * Return true if astr not a suffix of bstr
 *   (compatibility with historical functions).
 */
bool str_suffix( const char *astr, const char *bstr )
{
    int                     sstr1 = 0;
    int                     sstr2 = 0;

    sstr1 = strlen( astr );
    sstr2 = strlen( bstr );
    if ( sstr1 <= sstr2 && !str_cmp( astr, bstr + sstr2 - sstr1 ) )
        return false;
    else
        return true;
}

/*
 * Returns an initial-capped string.
 */
char                   *capitalize( const char *str )
{
    static char             strcap[MAX_STRING_LENGTH] = "\0\0\0\0\0\0\0";
    int                     i = 0;

    for ( i = 0; str[i] != '\0'; i++ )
        strcap[i] = LOWER( str[i] );
    strcap[i] = '\0';
    strcap[0] = UPPER( strcap[0] );
    return strcap;
}

/*
 * Removes the tildes from a string.
 * Used for player-entered strings that go into disk files.
 */
void smash_tilde( char *str )
{
    for ( ; *str != '\0'; str++ )
    {
        if ( *str == '~' )
            *str = '-';
    }

    return;
}

/* procedures and constants needed for buffering */

/* local procedure for finding the next acceptable size */
/* -1 indicates out-of-boundary error */
int get_size( int val )
{
    int                     i = 0;

    for ( i = 0; i < MAX_BUF_LIST; i++ )
        if ( buf_size[i] >= val )
        {
            return buf_size[i];
        }

    return -1;
}

BUFFER                 *new_buf( void )
{
    BUFFER                 *buffer = NULL;

    if ( buf_free == NULL )
        buffer = ( BUFFER * ) alloc_perm( sizeof( *buffer ) );
    else
    {
        buffer = buf_free;
        buf_free = buf_free->next;
    }

    buffer->next = NULL;
    buffer->state = BUFFER_SAFE;
    buffer->size = get_size( BASE_BUF );

    buffer->string = ( char * ) alloc_mem( buffer->size );
    buffer->string[0] = '\0';
    VALIDATE( buffer );

    return buffer;
}

BUFFER                 *new_buf_size( int size )
{
    BUFFER                 *buffer = NULL;

    if ( buf_free == NULL )
        buffer = ( BUFFER * ) alloc_perm( sizeof( *buffer ) );
    else
    {
        buffer = buf_free;
        buf_free = buf_free->next;
    }

    buffer->next = NULL;
    buffer->state = BUFFER_SAFE;
    buffer->size = get_size( size );
    if ( buffer->size == -1 )
    {
        proper_exit( MUD_HALT, "new_buf: buffer size %d too large.", size );
    }
    buffer->string = ( char * ) alloc_mem( buffer->size );
    buffer->string[0] = '\0';
    VALIDATE( buffer );

    return buffer;
}

void free_buf( BUFFER *buffer )
{
    if ( !IS_VALID( buffer ) )
        return;

    free_mem( buffer->string, buffer->size );
    buffer->string = NULL;
    buffer->size = 0;
    buffer->state = BUFFER_FREED;
    INVALIDATE( buffer );

    buffer->next = buf_free;
    buf_free = buffer;
}

bool add_buf( BUFFER *buffer, const char *string )
{
    int                     len = 0;
    char                   *oldstr = NULL;
    int                     oldsize = 0;

    oldstr = buffer->string;
    oldsize = buffer->size;

    if ( buffer->state == BUFFER_OVERFLOW )            /* don't waste time on bad
                                                        * strings! */
        return false;

    len = strlen( buffer->string ) + strlen( string ) + 1;

    while ( len >= buffer->size )                      /* increase the buffer size */
    {
        buffer->size = get_size( buffer->size + 1 );
        {
            if ( buffer->size == -1 )                  /* overflow */
            {
                buffer->size = oldsize;
                buffer->state = BUFFER_OVERFLOW;
                log_error( "Buffer overflow past size %d", buffer->size );
                return false;
            }
        }
    }

    if ( buffer->size != oldsize )
    {
        buffer->string = ( char * ) alloc_mem( buffer->size );

        strcpy( buffer->string, oldstr );
        free_mem( oldstr, oldsize );
    }

    strcat( buffer->string, string );
    return true;
}

void clear_buf( BUFFER *buffer )
{
    buffer->string[0] = '\0';
    buffer->state = BUFFER_SAFE;
}

char                   *buf_string( BUFFER *buffer )
{
    return buffer->string;
}