/***************************************************************************
 *  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.                                                  *
 ***************************************************************************/

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

extern int _filbuf args((FILE *));

/*
 * Globals.
 */
char bug_buf[2 * MAX_INPUT_LENGTH];
char *help_greeting;


/*
 * Locals.
 */
char *string_hash[MAX_KEY_HASH];
bool fBootDb;
char *string_space;
char *top_string;
char str_empty[1];


/*
 * Memory management.
 * Increase MAX_STRING if you have too.
 * Tune the others only if you understand what you're doing.
 */
#define			MAX_STRING	1348576
#define			MAX_PERM_BLOCK	131072
#define			MAX_MEM_TORLIST	11

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

int nAllocString;
int sAllocString;
int nAllocPerm;
int sAllocPerm;



/*
 * Big mama top level function.
 */
void boot_db(void)
{
    /*
     * Init some data space stuff.
     */
    {
	if ((string_space = (char *) calloc(1, MAX_STRING)) == NULL)
	    exit(1);
	top_string = string_space;
	fBootDb = true;
	}
	fBootDb = false;
	load_accnt_cmds();
	load_greet();
	//load_forums();
	//load_notes();
    return;
}

void load_greet()
{	FILE *fp;
	char buf[MSL];
	sprintf(buf, "%sgreet.dat", DATA_DIR ); /* Greet is kept from a constant, cause may add random greets */
	if( ( fp = fopen(buf, "r" ) ) != NULL)
	{	SREAD(help_greeting);
		return;
	}
	perror(buf);
	exit(1); /* If the greeting isn't read, we'll shut her down! Cause, ya know, you can't run a mud without a greet! */
}

/*
 * Allocate some ordinary memory,
 *   with the expectation of freeing it someday.
 */
void *alloc_mem(int sMem)
{
    void *pMem;
    int iList;

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

    if (iList == MAX_MEM_TORLIST)
    {
	exit(1);
    }

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

    return pMem;
}



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

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

    if (iList == MAX_MEM_TORLIST)
    {
	exit(1);
    }

    pMem = memset(pMem, 0, 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(int sMem)
{
    static char *pMemPerm;
    static int iMemPerm;
    void *pMem;

    while (sMem % sizeof(long) != 0)
	sMem++;
    if (sMem > MAX_PERM_BLOCK)
    {
	exit(1);
    }

    if (pMemPerm == NULL || iMemPerm + sMem > MAX_PERM_BLOCK)
    {
	iMemPerm = 0;
	if ((pMemPerm = (char *) calloc(1, MAX_PERM_BLOCK)) == NULL)
	{
	    perror("Alloc_perm");
	    exit(1);
	}
    }

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



/*
 * Duplicate a string into dynamic memory.
 */
char *str_dup(const char *str)
{
    char *str_new;

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

    if (str >= string_space && str < top_string)
	return (char *) str;

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



/*
 * This function is here to aid in debugging.
 * If the last expression in a function is another function call,
 *   gcc likes to generate a JMP instead of a CALL.
 * This is called "tail chaining."
 * It hoses the debugger call stack for that call.
 * So I make this the last call in certain critical functions,
 *   where I really need the call stack to be right for debugging!
 *
 * If you don't understand this, then LEAVE IT ALONE.
 * Don't remove any calls to tail_chain anywhere.
 *
 * -- Furey
 */
void tail_chain(void)
{
    return;
}



/*
 * Return true if an argument is completely numeric.
 */
bool is_number(char *arg)
{
    if (*arg == '\0')
	return false;

    if (*arg == '+' || *arg == '-')
	arg++;

    for (; *arg != '\0'; arg++)
    {
	if (!isdigit(*arg))
	    return false;
    }

    return true;
}



/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.
 */
char *one_argument(char *argument, char *arg_first)
{
    char cEnd;

    while (isspace(*argument))
	argument++;

    cEnd = ' ';
    if (*argument == '\'' || *argument == '"')
	cEnd = *argument++;

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

    while (isspace(*argument))
	argument++;

    return argument;
}


/* Thanks to ROM Here! Return FALSE if they match!*/
bool str_cmp( const char *astr, const char *bstr )
{
    if ( astr == NULL )
    	return true;
    

    if ( bstr == NULL )
    	return true;
    

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

    return false;
}

/*
 * Thanks to ROM here once again.
 */
bool str_prefix( const char *astr, const char *bstr )
{
    if ( astr == NULL )
		return true;
   
    if ( bstr == NULL )
		return true;
    
    for ( ; *astr; astr++, bstr++ )
		if ( LOWER(*astr) != LOWER(*bstr) )
		    return true;
  
    return false;
}

/* Cudos to Rom again! */
char *fread_word( FILE *fp )
{
    static char word[MAX_INPUT_LENGTH];
    char *pword;
    char cEnd;

    do
    {
	cEnd = getc( fp );
    }
    while ( isspace( cEnd ) );

    if ( cEnd == '\'' || cEnd == '"' )
    {
	pword   = word;
    }
    else
    {
	word[0] = cEnd;
	pword   = word+1;
	cEnd    = ' ';
    }

    for ( ; pword < word + MAX_INPUT_LENGTH; pword++ )
    {
	*pword = getc( fp );
	if ( cEnd == ' ' ? isspace(*pword) : *pword == cEnd )
	{
	    if ( cEnd == ' ' )
		ungetc( *pword, fp );
	    *pword = '\0';
	    return word;
	}
    }

    exit( 1 );
    return NULL;
}

/*
 * Read and allocate space for a string from a file.
 * These strings are read-only and shared.
 * Strings are hashed:
 *   each string prepended with hash pointer to prev string,
 *   hash code is simply the string length.
 *   this function takes 40% to 50% of boot-up time.
 */
char *fread_string( FILE *fp )
{
    char *plast;
    char c;

    plast = top_string + sizeof(char *);
    if ( plast > &string_space[MAX_STRING - MAX_STRING_LENGTH] )
		exit( 1 );


    /*
     * Skip blanks.
     * Read first char.
     */
    do
    {
	c = getc( fp );
    }
    while ( isspace(c) );

    if ( ( *plast++ = c ) == '~' )
	return &str_empty[0];

    for ( ;; )
    {
        /*
         * Back off the char type lookup,
         *   it was too dirty for portability.
         *   -- Furey
         */

	switch ( *plast = getc(fp) )
	{
        default:
            plast++;
            break;
 
        case EOF:
	    return NULL;
            /* exit( 1 ); */
            break;
 
        case '\n':
            plast++;
            *plast++ = '\r';
            break;
 
        case '\r':
            break;
 
        case '~':
            plast++;
	    {
		union
		{
		    char *	pc;
		    char	rgc[sizeof(char *)];
		} u1;
		int ic;
		int iHash;
		char *pHash;
		char *pHashPrev;
		char *pString;

		plast[-1] = '\0';
		iHash     = UMIN( MAX_KEY_HASH - 1, plast - 1 - top_string );
		for ( pHash = string_hash[iHash]; pHash; pHash = pHashPrev )
		{
		    for ( ic = 0; ic < (int)sizeof(char *); ic++ )
			u1.rgc[ic] = pHash[ic];
		    pHashPrev = u1.pc;
		    pHash    += (int)sizeof(char *);

		    if ( top_string[sizeof(char *)] == pHash[0]
		    &&   !strcmp( top_string+sizeof(char *)+1, pHash+1 ) )
			return pHash;
		}

		if ( fBootDb )
		{
		    pString		= top_string;
		    top_string		= plast;
		    u1.pc		= string_hash[iHash];
		    for ( ic = 0; ic < (int)sizeof(char *); ic++ )
			pString[ic] = u1.rgc[ic];
		    string_hash[iHash]	= pString;

		    nAllocString += 1;
		    sAllocString += top_string - pString;
		    return pString + sizeof(char *);
		}
		else
		{
		    return str_dup( top_string + sizeof(char *) );
		}
	    }
	}
    }
}

int fread_number( FILE *fp )
{
    int number;
    bool sign;
    char c;

    do
    {
	c = getc( fp );
    }
    while ( isspace(c) );

    number = 0;

    sign   = false;
    if ( c == '+' )
    {
	c = getc( fp );
    }
    else if ( c == '-' )
    {
	sign = true;
	c = getc( fp );
    }

    if ( !isdigit(c) )
		exit( 1 );
    

    while ( isdigit(c) )
    {
	number = number * 10 + c - '0';
	c      = getc( fp );
    }

    if ( sign )
	number = 0 - number;

    if ( c == '|' )
	number += fread_number( fp );
    else if ( c != ' ' )
	ungetc( c, fp );

    return number;
}

void fread_to_eol( FILE *fp )
{
    char c;

    do
    {
        c = getc( fp );
    }
    while ( c != '\n' && c != '\r' );

    do
    {
        c = getc( fp );
    }
    while ( c == '\n' || c == '\r' );

    ungetc( c, fp );
    return;
}