1stMUD4.0/bin/
1stMUD4.0/doc/MPDocs/
1stMUD4.0/player/
1stMUD4.0/win32/
1stMUD4.0/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-2003 by Ryan Jennings              *
*            http://1stmud.dlmud.com/  <r-jenn@shaw.ca>                   *
***************************************************************************/

#include "merc.h"
#include "interp.h"
#include "signals.h"

PROTOTYPE(bool check_social, (CHAR_DATA *, char *, const char *));
PROTOTYPE(bool check_disabled, (CHAR_DATA *, CMD_DATA *));

#define END_MARKER	"END"		/* for load_disabled() and save_disabled() */

/*
 * Log-all switch.
 */

CH_CMD(do_null)
{
	chprintln(ch, "This is not a command, notify the immortals.");
	return;
}

bool cmd_level_ok(CHAR_DATA * ch, CMD_DATA * cmd)
{
	return (get_trust(ch) >= cmd->level);
}

/*
 * The main entry point for executing commands.
 * Can be recursively called from 'at', 'order', 'force'.
 */
void interpret(CHAR_DATA * ch, const char *argument)
{
	char command[MAX_INPUT_LENGTH];
	char logline[MAX_INPUT_LENGTH];
	CMD_DATA *cmd;

	/*
	 * Strip leading spaces.
	 */
	while (isspace(*argument))
		argument++;
	if (IS_NULLSTR(argument))
		return;

	/*
	 * No hiding.
	 */
	REMOVE_BIT(ch->affected_by, AFF_HIDE);

	/*
	 * Implement freeze command.
	 */
	if (!IS_NPC(ch) && IS_SET(ch->act, PLR_FREEZE))
	{
		chprintln(ch, "You're totally frozen!");
		return;
	}

	strcpy(logline, argument);
	crash_info.desc = ch->desc;
	strcpy(crash_info.shrt_cmd, logline);
	sprintf(crash_info.long_cmd, "[%5ld] %s in [%5ld] %s: %s",
			IS_NPC(ch) ? ch->pIndexData->vnum : 0, IS_NPC(ch) ?
			ch->short_descr : ch->name, ch->in_room ?
			ch->in_room->vnum : 0, ch->in_room ? ch->in_room->name :
			"(not in a room)", logline);
	crash_info.cmd_status = 2;

	/*
	 * Grab the command word.
	 * Special parsing so ' can be a command,
	 *   also no spaces needed after punctuation.
	 */
	if (!isalpha(argument[0]) && !isdigit(argument[0]))
	{
		command[0] = argument[0];
		command[1] = '\0';
		argument++;
		while (isspace(*argument))
			argument++;
	}
	else
	{
		argument = one_argument(argument, command);
	}

	/*
	 * Look for command in command table.
	 */
	for (cmd = command_hash[LOWER(command[0]) % 126]; cmd; cmd = cmd->next_hash)
	{
		if (!str_prefix(command, cmd->name) && cmd_level_ok(ch, cmd))
			break;
	}

	/*
	 * Log and snoop.
	 */
	if (cmd)
	{
		if (cmd->log == LOG_NEVER)
			strcpy(logline, "");

		if ((!IS_NPC(ch) && IS_SET(ch->act, PLR_LOG))
			|| IS_SET(mud_info.mud_flags, MUD_LOGALL) || cmd->log == LOG_ALWAYS)
		{
			sprintf(log_buf, "Log %s: %s", ch->name, logline);
			wiznet(log_buf, ch, NULL, WIZ_SECURE, 0, get_trust(ch));
			log_string(log_buf);
		}

	}

	if (ch->desc != NULL && ch->desc->snoop_by != NULL)
	{
		d_print(ch->desc->snoop_by, "% ", 2);
		d_println(ch->desc->snoop_by, logline, 0);
	}
	if (!cmd)
	{
		/*
		 * Look for command in socials table.
		 */
		if (!check_social(ch, command, argument))
			chprintln(ch, "Huh?");
		return;
	}
	else if (check_disabled(ch, cmd))
	{
		chprintln(ch, "This command has been temporarily disabled.");
		return;
	}

	/*
	 * Character not in position for command?
	 */
	if (ch->position < cmd->position)
	{
		switch (ch->position)
		{
		case POS_DEAD:
			chprintln(ch, "Lie still; you are DEAD.");
			break;

		case POS_MORTAL:
		case POS_INCAP:
			chprintln(ch, "You are hurt far too bad for that.");
			break;

		case POS_STUNNED:
			chprintln(ch, "You are too stunned to do that.");
			break;

		case POS_SLEEPING:
			chprintln(ch, "In your dreams, or what?");
			break;

		case POS_RESTING:
			chprintln(ch, "Nah... You feel too relaxed...");
			break;

		case POS_SITTING:
			chprintln(ch, "Better stand up first.");
			break;

		case POS_FIGHTING:
			chprintln(ch, "No way!  You are still fighting!");
			break;

		default:
			break;
		}
		return;
	}

	crash_info.cmd_status = 1;

	/*
	 * Dispatch the command.
	 */
	(*cmd->do_fun) (ch, argument);

	crash_info.cmd_status = 3;

	strcat(crash_info.shrt_cmd, " (Finished)");
	strcat(crash_info.long_cmd, " (Finished)");

	crash_info.cmd_status = 0;

	tail_chain();
	return;
}

/* function to keep argument safe in all commands -- no static strings */
void do_function(CHAR_DATA * ch, DO_FUN * do_fun, const char *argument)
{
	const char *command_string;

	/* copy the string */
	command_string = str_dup(argument);

	/* dispatch the command */
	(*do_fun) (ch, command_string);

	/* free the string */
	free_string(command_string);
}

SOCIAL_DATA *find_social(const char *command)
{
	SOCIAL_DATA *social;
	int hash;

	if (LOWER(command[0]) < 'a' || LOWER(command[0]) > 'z')
		hash = 0;
	else
		hash = (LOWER(command[0]) - 'a') + 1;

	for (social = social_hash[hash]; social; social = social->next_hash)
	{
		if (!str_prefix(command, social->name))
			return social;
	}
	return NULL;
}

bool check_social(CHAR_DATA * ch, char *command, const char *argument)
{
	char arg[MAX_INPUT_LENGTH];
	CHAR_DATA *victim;
	SOCIAL_DATA *cmd;

	if ((cmd = find_social(command)) == NULL)
		return FALSE;

	if (!IS_NPC(ch) && IS_SET(ch->comm, COMM_NOEMOTE))
	{
		chprintln(ch, "You are anti-social!");
		return TRUE;
	}

	switch (ch->position)
	{
	case POS_DEAD:
		chprintln(ch, "Lie still; you are DEAD.");
		return TRUE;

	case POS_INCAP:
	case POS_MORTAL:
		chprintln(ch, "You are hurt far too bad for that.");
		return TRUE;

	case POS_STUNNED:
		chprintln(ch, "You are too stunned to do that.");
		return TRUE;

	case POS_SLEEPING:
		/*
		 * I just know this is the path to a 12" 'if' statement.  :(
		 * But two players asked for it already!  -- Furey
		 */
		if (!str_cmp(cmd->name, "snore"))
			break;
		chprintln(ch, "In your dreams, or what?");
		return TRUE;
	default:
		break;
	}

	one_argument(argument, arg);
	victim = NULL;
	if (IS_NULLSTR(arg))
	{
		act(cmd->others_no_arg, ch, NULL, victim, TO_ROOM);
		act(cmd->char_no_arg, ch, NULL, victim, TO_CHAR);
	}
	else if ((victim = get_char_room(ch, NULL, arg)) == NULL)
	{
		chprintln(ch, "They aren't here.");
	}
	else if (victim == ch)
	{
		act(cmd->others_auto, ch, NULL, victim, TO_ROOM);
		act(cmd->char_auto, ch, NULL, victim, TO_CHAR);
	}
	else
	{
		if (is_ignoring(victim, ch->name, IGNORE_SOCIALS))
		{
			act("$N is ignoring socials from you.", ch, NULL, victim, TO_CHAR);
			return TRUE;
		}
		act(cmd->others_found, ch, NULL, victim, TO_NOTVICT);
		act(cmd->char_found, ch, NULL, victim, TO_CHAR);
		act(cmd->vict_found, ch, NULL, victim, TO_VICT);

		if (!IS_NPC(ch) && IS_NPC(victim) &&
			!IS_AFFECTED(victim, AFF_CHARM) && IS_AWAKE(victim) &&
			victim->desc == NULL)
		{
			switch (number_bits(4))
			{
			case 0:

			case 1:
			case 2:
			case 3:
			case 4:
			case 5:
			case 6:
			case 7:
			case 8:
				act(cmd->others_found, victim, NULL, ch, TO_NOTVICT);
				act(cmd->char_found, victim, NULL, ch, TO_CHAR);
				act(cmd->vict_found, victim, NULL, ch, TO_VICT);
				break;

			case 9:
			case 10:
			case 11:
			case 12:
				act("$n slaps $N.", victim, NULL, ch, TO_NOTVICT);
				act("You slap $N.", victim, NULL, ch, TO_CHAR);
				act("$n slaps you.", victim, NULL, ch, TO_VICT);
				break;
			}
		}
	}

	return TRUE;
}

/*
 * Return true if an argument is completely numeric.
 */
bool is_number(const char *arg)
{

	if (*arg == '\0')
		return FALSE;

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

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

	return TRUE;
}

static unsigned int x_argument(const char *argument,
							   char arg[MAX_INPUT_LENGTH], char c)
{
	char *p;
	char *q;
	int number;

	p = strchr(argument, c);
	if (p == NULL)
	{
		strcpy(arg, argument);
		return 1;
	}

	number = strtoul(argument, &q, 0);
	if (q != p)
		number = 0;
	strncpy(arg, p + 1, MAX_INPUT_LENGTH);
	return number;
}

/*
 * Given a string like 14.foo, return 14 and 'foo'
 */
unsigned int number_argument(const char *argument, char *arg)
{
	return x_argument(argument, arg, '.');
}

/* 
 * Given a string like 14*foo, return 14 and 'foo'
*/
unsigned int mult_argument(const char *argument, char *arg)
{
	return x_argument(argument, arg, '*');
}

/*
 * Pick off one argument from a string and return the rest.
 * Understands quotes.
 */
const char *one_argument(const 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;
}

/*
 * Contributed by Alander.
 */
CH_CMD(do_commands)
{
	CMD_DATA *cmd;
	int col;

	col = 0;
	for (cmd = cmd_first_sorted; cmd; cmd = cmd->next_sort)
	{
		if (cmd->level < LEVEL_HERO && cmd_level_ok(ch, cmd) && cmd->show)
		{
			chprintf(ch, "%-12s", cmd->name);
			if (++col % 6 == 0)
				chprintln(ch, "");
		}
	}

	if (col % 6 != 0)
		chprintln(ch, "");

	return;
}

CH_CMD(do_wizhelp)
{
	CMD_DATA *cmd;
	int col;

	col = 0;
	for (cmd = cmd_first_sorted; cmd; cmd = cmd->next_sort)
	{
		if (cmd->level >= LEVEL_HERO && cmd_level_ok(ch, cmd) && cmd->show)
		{
			chprintf(ch, "%-12s", cmd->name);
			if (++col % 6 == 0)
				chprintln(ch, "");
		}
	}

	if (col % 6 != 0)
		chprintln(ch, "");

	return;
}

/* Syntax is:
disable - shows disabled commands
disable <command> - toggles disable status of command
*/

CH_CMD(do_disable)
{
	CMD_DATA *i;
	DISABLED_DATA *p;
	char arg[MIL];

	if (IS_NPC(ch))
	{
		chprintln(ch, "RETURN first.");
		return;
	}

	argument = one_argument(argument, arg);

	if (IS_NULLSTR(arg))		/* Nothing specified. Show disabled commands. */
	{
		if (!disabled_first)	/* Any disabled at all ? */
		{
			chprintln(ch, "There are no commands disabled.");
			return;
		}

		chprintln(ch, "Disabled commands:\n\r"
				  "Command      Level   Disabled by   Disabled for");

		for (p = disabled_first; p; p = p->next)
		{
			chprintlnf(ch, "%-12s %5d   %-12s  %s", p->command->name,
					   p->level, p->disabled_by, p->disabled_for);
		}
		return;
	}

	/* command given */

	/* First check if it is one of the disabled commands */
	for (p = disabled_first; p; p = p->next)
		if (!str_cmp(arg, p->command->name))
			break;

	if (p)						/* this command is disabled */
	{
		/* Optional: The level of the imm to enable the command must equal or exceed level
		   of the one that disabled it */

		if (get_trust(ch) < p->level)
		{
			chprintln(ch, "This command was disabled by a higher power.");
			return;
		}

		/* Remove */
		UNLINK(p, disabled_first, disabled_last, next, prev);
		free_string(p->disabled_by);	/* free name of disabler */
		free_string(p->disabled_for);
		free_mem(p);			/* free node */
		save_disabled();		/* save to disk */
		chprintln(ch, "Command enabled.");
	}
	else						/* not a disabled command, check if that command exists */
	{

		/* Search for the command */
		for (i = command_hash[LOWER(argument[0]) % 126]; i; i = i->next_hash)
			if (!str_cmp(i->name, argument))
				break;
		/* Found? */
		if (!i)
		{
			chprintln(ch, "No such command.");
			return;
		}

		/* IQ test */
		if (i->do_fun == do_disable)
		{
			chprintln(ch, "You cannot disable the disable command.");
			return;
		}

		/* Can the imm use this command at all ? */
		if (i->level > get_trust(ch))
		{
			chprintln(ch,
					  "You don't have access to that command; you cannot disable it.");
			return;
		}

		/* Disable the command */
		alloc_mem(p, DISABLED_DATA, 1);

		p->command = i;
		p->disabled_by = str_dup(ch->name);	/* save name of disabler */
		p->disabled_for = str_dup(argument);
		p->level = get_trust(ch);	/* save trust */
		LINK(p, disabled_first, disabled_last, next, prev);

		chprintln(ch, "Command disabled.");
		save_disabled();		/* save to disk */
	}
}

/* Check if that command is disabled 
   Note that we check for equivalence of the do_fun pointers; this means
   that disabling 'chat' will also disable the '.' command
*/
bool check_disabled(CHAR_DATA * ch, CMD_DATA * command)
{
	DISABLED_DATA *p;

	for (p = disabled_first; p; p = p->next)
		if (p->command->do_fun == command->do_fun)
			break;

	if (!p)
		return FALSE;

	if (!ch || IS_NULLSTR(p->disabled_for))
		return TRUE;

	return is_exact_name(ch->name, p->disabled_for);
}

/* Load disabled commands */
void load_disabled()
{
	READ_DATA *fp;
	DISABLED_DATA *p;
	char *name;
	CMD_DATA *i;

	disabled_first = NULL;

	fp = open_read(DISABLED_FILE);

	if (!fp)					/* No disabled file.. no disabled commands : */
		return;

	name = read_word(fp);

	while (str_cmp(name, END_MARKER))	/* as long as name is NOT END_MARKER :) */
	{
		/* Find the command in the table */
		for (i = command_hash[LOWER(name[0]) % 126]; i; i = i->next_hash)
			if (!str_cmp(i->name, name))
				break;

		if (!i)					/* command does not exist? */
		{
			bug("Skipping uknown command in " DISABLED_FILE " file.");
			read_to_eol(fp);
		}
		else					/* add new disabled command */
		{
			alloc_mem(p, DISABLED_DATA, 1);
			p->command = i;
			p->level = read_number(fp);
			p->disabled_by = str_dup(read_word(fp));
			p->disabled_for = read_string(fp);
			LINK(p, disabled_first, disabled_last, next, prev);
		}

		name = read_word(fp);
	}

	close_read(fp);
}

/* Save disabled commands */
void save_disabled()
{
	WRITE_DATA *fp;
	DISABLED_DATA *p;

	if (!disabled_first)		/* delete file if no commands are disabled */
	{
		unlink(DISABLED_FILE);
		return;
	}

	fp = open_write(DISABLED_FILE);

	if (!fp)
	{
		bug("Could not open " DISABLED_FILE " for writing");
		return;
	}

	for (p = disabled_first; p; p = p->next)
		fprintf(fp->stream, "'%s' %d '%s' %s~\n", p->command->name, p->level,
				p->disabled_by, p->disabled_for);

	fprintf(fp->stream, "%s\n", END_MARKER);

	close_write(fp);
}