1stMUD/corefiles/
1stMUD/gods/
1stMUD/player/
1stMUD/win32/
1stMUD/win32/ROM/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements 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          *
*  benefiting.  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                    *
***************************************************************************
*       1stMUD ROM Derivative (c) 2001-2002 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/
/***************************************************************************
 *  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 "tables.h"
#include "olc.h"

const char *string_linedel (const char *, int);
const char *string_lineadd (const char *, const char *, int);
const char *numlineas (const char *);

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

	if (*pString == NULL)
	{
		*pString = str_dup ("");
	}
	else
	{
		*(char *) *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, const char **pString)
{
	chprintln (ch, "-=======- Entering APPEND Mode -========-");
	chprintln (ch, "    Type .h on a new line for help");
	chprintln (ch, " Terminate with a .q or @ on a blank line.");
	chprintln (ch, "-=======================================-");

	if (*pString == NULL)
	{
		*pString = str_dup ("");
	}
	chprint (ch, numlineas (*pString));

/* numlineas entrega el string con \n\r */
/*  if ( *(*pString + strlen( *pString ) - 1) != '\r' )
	chprint( "\n\r", ch ); */

	ch->desc->pString = pString;

	return;
}

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

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

	return str_dup (xbuf);
}

#define PARSE_FORMAT		0
#define PARSE_REPLACE		1
#define PARSE_REPLACE_ALL   2
#define PARSE_HELP		    3
#define PARSE_DELETE		4
#define PARSE_INSERT		5
#define PARSE_LIST_NORM		6
#define PARSE_LIST_NUM		7
#define PARSE_EDIT		    8

const char *del_last_line (const char *string)
{
	int len;
	bool found = FALSE;

	char xbuf[MSL];

	xbuf[0] = '\0';
	if (IS_NULLSTR (string))
		return (str_dup (xbuf));

	strcpy (xbuf, string);

	for (len = strlen (xbuf); len > 0; len--)
	{
		if (xbuf[len] == '\r')
		{
			if (!found)
			{

				if (len > 0)
					len--;
				found = TRUE;
			}
			else
			{

				xbuf[len + 1] = '\0';
				free_string (string);
				return (str_dup (xbuf));
			}
		}
	}
	xbuf[0] = '\0';
	free_string (string);
	return (str_dup (xbuf));
}

const char *string_replace_all (const char *orig, char *old, char *newstr)
{
	if (!strstr (orig, old) || !str_cmp (old, newstr))
	{
		return orig;
	}

	while (strstr (orig, old))
	{
		orig = string_replace (orig, old, newstr);
	}
	return orig;
}

void parse_action (const char **text, int command, const char *string,
				   CHAR_DATA * ch)
{
	char arg1[MAX_INPUT_LENGTH], arg2[MAX_INPUT_LENGTH];

	switch (command)
	{
	case PARSE_HELP:
		chprintln (ch,
				   "-------------------------------------------------------");
		chprintln (ch,
				   "Edit help (commands on blank line):\n\r"
				   ".h               - get help (this info)\n\r"
				   ".s               - show string so far\n\r"
				   ".S               - show string so far without line numbers\n\r"
				   ".f               - formats text\n\r"
				   ".c               - clear string so far\n\r"
				   ".d#              - delete line number <num>\n\r"
				   ".d               - delete last line\n\r"
				   ".i# <str>        - insert <str> on line <num>\n\r"
				   ".e# <str>        - replace line <num> with <str>\n\r"
				   ".r 'a' 'b'       - replace first occurance of text\n\r"
				   ".R 'a' 'b'       - replace all occurances of text\n\r"
				   "                   usage: .r 'pattern' 'replacement'\n\r"
				   ".| <command>     - execute a mud command\n\r"
				   ".q               - end string");
		chprintln (ch,
				   "------------------------------------------------------");
		break;
	case PARSE_FORMAT:
		*text = format_string (*text);
		chprintln (ch, "String formatted.");
		break;
	case PARSE_EDIT:
		string = one_argument (string, arg1);
		if (arg1[0] == '\0')
		{
			chprintln (ch, "You must specify a line number.");
			return;
		}
		*text = string_linedel (*text, atoi (arg1));
		*text = string_lineadd (*text, string, atoi (arg1));
		chprintln (ch, "Line replaced.");
		break;
	case PARSE_DELETE:
		if (string[0] == '\0')
			*text = del_last_line (*text);
		else
			*text = string_linedel (*text, atoi (string));
		chprintln (ch, "Line deleted.");
		break;
	case PARSE_REPLACE:
		string = first_arg (string, arg1, FALSE);
		string = first_arg (string, arg2, FALSE);
		if (arg1[0] == '\0')
		{
			chprintln (ch, "Usage: .r 'old string' 'new string'");
			return;
		}
		*text = string_replace (*text, arg1, arg2);
		chprintlnf (ch, "'%s' replaced with '%s'.", arg1, arg2);
		break;
	case PARSE_REPLACE_ALL:
		string = first_arg (string, arg1, FALSE);
		string = first_arg (string, arg2, FALSE);
		if (arg1[0] == '\0')
		{
			chprintln (ch, "Usage: .R 'old string' 'new string'");
			return;
		}
		*text = string_replace_all (*text, arg1, arg2);
		chprintlnf (ch, "All occurances of '%s' replaced with '%s'.",
					arg1, arg2);
		break;
	case PARSE_INSERT:
		string = first_arg (string, arg1, FALSE);
		*text = string_lineadd (*text, string, atoi (arg1));
		chprintln (ch, "Line inserted.");
		break;
	case PARSE_LIST_NORM:
		chprintln (ch, "String so far:");
		page_to_char (*text, ch);
		break;
	case PARSE_LIST_NUM:
		chprintln (ch, "String so far:");
		page_to_char (numlineas (*text), ch);
		break;
	default:
		chprintln (ch, "Invalid command.");
		bug ("invalid command passed", 0);
		break;
	}
}

int parse_string_command (const char **text, const char *str, CHAR_DATA * ch)
{
	int i = 2, j = 0;
	char actions[MAX_INPUT_LENGTH];

	if ((*str == '.'))
	{
		while (str[i] != '\0')
		{
			actions[j] = str[i];
			i++;
			j++;
		}
		actions[j] = '\0';
		*(char *) str = '\0';
		switch (str[1])
		{
		case 'q':
			*(char *) str = '\0';
			return STRING_END;
		case '|':
			interpret (ch, actions);
			chprintln (ch, "Command performed.");
			return STRING_FOUND;
		case 'c':
			chprintln (ch, "String cleared.");
			replace_string (*text, "");
			return STRING_FOUND;
		case 's':
			parse_action (text, PARSE_LIST_NUM, actions, ch);
			return STRING_FOUND;
		case 'S':
			parse_action (text, PARSE_LIST_NORM, actions, ch);
			return STRING_FOUND;
		case 'r':
			parse_action (text, PARSE_REPLACE, actions, ch);
			return STRING_FOUND;
		case 'R':
			parse_action (text, PARSE_REPLACE_ALL, actions, ch);
			return STRING_FOUND;
		case 'f':
			parse_action (text, PARSE_FORMAT, actions, ch);
			return STRING_FOUND;
		case 'd':
			parse_action (text, PARSE_DELETE, actions, ch);
			return STRING_FOUND;
		case 'i':
			parse_action (text, PARSE_INSERT, actions, ch);
			return STRING_FOUND;
		case 'e':
			parse_action (text, PARSE_EDIT, actions, ch);
			return STRING_FOUND;
		case 'h':
			parse_action (text, PARSE_HELP, actions, ch);
			return STRING_FOUND;
		default:
			chprintln (ch, "Invalid command.");
			return STRING_FOUND;
		}
	}
	else if (*str == '@')
	{
		*(char *) str = '\0';
		return STRING_END;
	}
	return STRING_NONE;
}

void string_add (CHAR_DATA * ch, char *argument)
{
	char buf[MSL];
	int action = 0;

	action = parse_string_command (ch->desc->pString, argument, ch);

	switch (action)
	{
	case STRING_END:
		if (ch->desc->editor == ED_MPCODE)
		{
			MOB_INDEX_DATA *mob;
			int hash;
			MPROG_LIST *mpl;
			MPROG_CODE *mpc;

			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)
							{
								chprintlnf (ch, "Fixing mob %ld.", mob->vnum);
								mpl->code = mpc->code;
							}
		}
		ch->desc->pString = NULL;
		chprintln (ch, "Done editing.");
		return;
	case STRING_FOUND:
		return;
	default:
	case STRING_NONE:
		strcpy (buf, *ch->desc->pString);

		/*
		 * Truncate strings to MAX_STRING_LENGTH.
		 * --------------------------------------
		 */
		if (strlen (buf) + strlen (argument) >= ((MSL * 2) - 6))
		{
			chprintln (ch, "String too long, last line skipped.");
			/* Force character out of editing mode. */
			ch->desc->pString = NULL;
			return;
		}

		/*
		 * Ensure no tilde's inside string.
		 * --------------------------------
		 */
		smash_tilde (argument);
		strcat (buf, argument);
		strcat (buf, "\n\r");
		free_string (*ch->desc->pString);
		*ch->desc->pString = str_dup (buf);
		return;
	}
}

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

	xbuf[0] = xbuf2[0] = 0;

	i = 0;

	for (rdesc = 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)
		{
			*(char *) (rdesc + i) = 0;
			strcat (xbuf, rdesc);
			strcat (xbuf, "\n\r");
			rdesc += i + 1;
			while (*rdesc == ' ')
				rdesc++;
		}
		else
		{
			bug ("No spaces", 0);
			*(char *) (rdesc + 75) = 0;
			strcat (xbuf, rdesc);
			strcat (xbuf, "-\n\r");
			rdesc += 76;
		}
	}
	while (*(rdesc + i) &&
		   (*(rdesc + i) == ' ' || *(rdesc + i) == '\n' ||
			*(rdesc + i) == '\r'))
		i--;
	*(char *) (rdesc + i + 1) = 0;
	strcat (xbuf, rdesc);
	if (xbuf[strlen (xbuf) - 2] != '\n')
		strcat (xbuf, "\n\r");

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

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

	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.
 */
const char *string_unpad (const char *argument)
{
	char buf[MAX_STRING_LENGTH];
	char *s;

	s = (char *) 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.
 */
const char *string_proper (const char *argument)
{
	char *s;

	s = (char *) argument;

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

	return argument;
}

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

	buf[0] = '\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);
}

const char *string_lineadd (const char *string, const char *newstr, int line)
{
	const char *strtmp = string;
	int cnt = 1, tmp = 0;
	bool done = FALSE;
	char buf[MAX_STRING_LENGTH];

	buf[0] = '\0';

	for (; *strtmp != '\0' || (!done && cnt == line); strtmp++)
	{
		if (cnt == line && !done)
		{
			strcat (buf, newstr);
			strcat (buf, "\n\r");
			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);
}

/* buf queda con la linea sin \n\r */
const char *getline (const char *str, char *buf);

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

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

	buf[0] = '\0';

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

	return buf;
}

/* Converts a ~ into a colour code for ~ */
void tilde_to_color (char *buffer, char *str)
{
	int i = 0;
	char buf[MSL];

	memset (buf, 0, MSL);

	if (!str || *str == '\0')
		return;

	while (*str != '\0')
	{
		if (*str == '~')
		{
			buf[i] = ANSI_KEY;
			i++;
			buf[i] = '-';
			i++;
			str++;
		}
		else
		{
			buf[i] = *str;
			i++;
			str++;
		}
	}
	strcpy (buffer, buf);
	return;
}

/* Removes all colour coding from a string */
void smash_colour (char *buffer, const char *str)
{
	int i = 0;
	char buf[MSL];

	memset (buf, 0, MSL);

	if (!str || *str == '\0')
		return;

	while (*str != '\0')
	{
		if (*str == ANSI_KEY)
		{
			str++;
			str++;
		}
		else if (*str == ANSI_CUSTOM)
		{
			str++;
			while (*str != ANSI_END)
				str++;
			str++;
		}
		else
		{
			buf[i] = *str;
			i++;
			str++;
		}
	}
	strcpy (buffer, buf);
	return;
}

/* Converts a colour code for a ~ into a ~ */
void color_to_tilde (char *buffer, char *str)
{
	int i = 0;
	char buf[MSL];

	memset (buf, 0, MSL);

	if (!str || *str == '\0')
		return;

	while (*str != '\0')
	{
		if (*str == ANSI_KEY)
		{
			str++;
			if (*str == '-')
			{
				buf[i] = '~';
				i++;
				str++;
			}
			else
				str++;
		}
		else
		{
			buf[i] = *str;
			i++;
			str++;
		}
	}
	strcpy (buffer, buf);
	return;
}

/* formats a string into a length and alignment
    *fill can be used as NULL, length '0' returns 77 */
void stringf (char *buffer, int length, int align, char *fill, char *string)
{
	char buf[MSL];
	char buf2[MSL];
	char *new_string;
	char *count_string;
	char temp;
	int count = 0, nCount = 0;
	int pos = 0;

	new_string = buf;
	count_string = buf2;
	strcpy (buf2, string);

	if (!fill)
		fill = " ";

	if (length == 0)
		length = 77;

	while (*count_string && nCount != length)
	{
		temp = *count_string++;

		if (temp == ANSI_KEY)
		{
			temp = *count_string++;
			if (temp == ANSI_KEY)
				nCount++;
			continue;
		}
		else if (temp == ANSI_CUSTOM)
		{
			temp = *count_string++;
			while (temp != ANSI_END)
				temp = *count_string++;
			continue;
		}
		nCount++;
	}

	if (align == ALIGN_RIGHT)
	{
		count = (length - ++nCount);
		while (nCount++ <= length)
		{
			buf[pos++] = *fill;
		}
	}

	if (align == ALIGN_CENTER)
	{
		nCount = (length - nCount) / 2;
		count = nCount;
		while (nCount-- > 0)
		{
			buf[pos++] = *fill;
		}
	}

	while (*string && count != length)
	{
		temp = *string++;
		buf[pos++] = temp;

		if (temp == ANSI_KEY)
		{
			temp = buf[pos] = *string++;

			if (temp == ANSI_KEY)
				count++;

			pos++;
			continue;
		}
		else if (temp == ANSI_CUSTOM)
		{
			temp = buf[pos] = *string++;

			while (temp != ANSI_END)
			{
				pos++;
				temp = buf[pos] = *string++;
			}
			pos++;
			continue;
		}
		count++;
	}

	while (count++ < length)
		buf[pos++] = *fill;

	buf[pos] = '\0';
	strcpy (buffer, new_string);
	return;
}

/* Counts the length of a string excluding colour codes */
int strlen_color (const char *string)
{
	int i, count = 0;
	char arg[MSL];

	if (!string)
		return count;

	strcpy (arg, string);

	for (i = 0; i <= MIL; i++)
	{

		if (arg[i] == '\0')
		{
			break;
		}

		else if (string[i] != ANSI_KEY && string[i] != ANSI_CUSTOM)
		{
			count++;
			continue;
		}

		else if (arg[i] == ANSI_KEY && arg[i + 1] == ANSI_KEY)
		{
			i++;
			count++;
			continue;
		}

		else if (arg[i] == ANSI_KEY && arg[i + 1] != ANSI_KEY &&
				 arg[i + 1] != '\0')
		{
			i += 1;
			continue;
		}
		else if (arg[i] == ANSI_CUSTOM)
		{
			i += 1;
			while (arg[i] != ANSI_END)
				i += 1;
			i += 1;
			continue;
		}
		else if (arg[i] == ANSI_KEY && arg[i + 1] == '\0')
		{
			break;
		}

		else
		{
			count++;
			continue;
		}
	}

	return count;
}

/* returns TRUE if the last char in a string i a colour code */
bool check_cstring (char *string)
{
	unsigned int i;

	if (string[0] == '\0')
	{
		return FALSE;
	}

	for (i = 0; i < strlen (string); i++)
	{

		if (string[i] == '\0')
		{
			return FALSE;
		}

		else if (string[i] != ANSI_KEY)
		{
			continue;
		}

		else if (string[i] == ANSI_KEY && string[i + 1] != '\0')
		{
			i++;
			continue;
		}

		else if (string[i] == ANSI_KEY && string[i + 1] == '\0')
		{
			return TRUE;
		}

		else
		{
			continue;
		}
	}

	return FALSE;
}

/* Can use colour coded fills like eg. draw_line("{w-{W+{R*{W+", 0); */

char *draw_line (char *fill, int len)
{
	static char buf_new[MSL];
	int mod;
	int l;
	char lbuf[MSL];
	int count;
	unsigned int m;

	buf_new[0] = '\0';

	if (!fill)
		fill = "-";

	if (len <= 0)
	{
		len = 79;
	}

	mod = len % strlen_color (fill);
	len /= strlen_color (fill);

	for (l = 0; l < len; l++)
		strcat (buf_new, fill);

	count = 0;
	lbuf[0] = '\0';

	for (m = 0; m < strlen (fill); m++)
	{
		if (fill[m] == '{')
		{
			lbuf[m] = fill[m];
			m++;
			lbuf[m] = fill[m];
			continue;
		}
		else
		{
			lbuf[m] = fill[m];
			count++;
			if (count >= mod)
				break;
		}
	}
	lbuf[m] = '{';
	lbuf[++m] = 'x';
	lbuf[++m] = '\0';

	strcat (buf_new, lbuf);
	return buf_new;
}