/***************************************************************************
 *  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 "merc.h"

extern int _filbuf args((FILE *));

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


/*
 * Locals.
 */
char *string_hash[MAX_KEY_HASH];

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_LIST	11

void *rgFreeList[MAX_MEM_LIST];
const int rgSizeList[MAX_MEM_LIST] = {
    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;
    }

    return;
}



/*
 * Clear a new character.
 */
void clear_char(CHAR_DATA * ch)
{
    static CHAR_DATA ch_zero;

    *ch = ch_zero;
    ch->name = &str_empty[0];
    ch->short_descr = &str_empty[0];
    ch->long_descr = &str_empty[0];
    ch->description = &str_empty[0];
    return;
}



/*
 * Free a character.
 */
void free_char(CHAR_DATA * ch)
{
    free_string(ch->name);
    free_string(ch->short_descr);
    free_string(ch->long_descr);
    free_string(ch->description);
    free_string(ch->pwd);

    ch->next = char_free;
    char_free = ch;
    return;
}



/*
 * 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_LIST; iList++)
    {
	if (sMem <= rgSizeList[iList])
	    break;
    }

    if (iList == MAX_MEM_LIST)
    {
	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_LIST; iList++)
    {
	if (sMem <= rgSizeList[iList])
	    break;
    }

    if (iList == MAX_MEM_LIST)
    {
	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;
}


/*
 * Extract a char from the world.
 */
void extract_char(CHAR_DATA * ch, bool fPull)
{
    if (ch == char_list)
    {
	char_list = ch->next;
    }
    else
    {
	CHAR_DATA *prev;

	for (prev = char_list; prev != NULL; prev = prev->next)
	{
	    if (prev->next == ch)
	    {
		prev->next = ch->next;
		break;
	    }
	}

	if (prev == NULL)
	{
	    return;
	}
    }

    if (ch->desc)
	ch->desc->character = NULL;
    free_char(ch);
    return;
}