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

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(WIN32)
#include <unistd.h>
#endif
#include <time.h>
#include "merc.h"
#include "interp.h"

bool check_social args((CHAR_DATA * ch, char *command, const char *argument));
bool check_disabled(const struct cmd_type *command);

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

/*
 * Log-all switch.
 */
char last_command[MAX_STRING_LENGTH];
char last_command2[MAX_INPUT_LENGTH];
DESCRIPTOR_DATA *last_descriptor;

struct cmd_type *cmd_table;

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

/*
 * 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];
	int cmd;
	int trust;
	bool found;

	/*
	 * Strip leading spaces.
	 */
	while (isspace(*argument))
		argument++;
	if (argument[0] == '\0')
		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;
	}

	/*
	 * Grab the command word.
	 * Special parsing so ' can be a command,
	 *   also no spaces needed after punctuation.
	 */
	strcpy(logline, argument);
	sprintf(last_command, "%s in room[%ld]: %s.", ch->name,
			ch->in_room->vnum, argument);
	if (ch->desc != NULL)
	{
		sprintf(last_command2, "%s", argument);
		last_descriptor = ch->desc;
	}
	else
	{
		last_command2[0] = '\0';
	}

	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.
	 */
	found = FALSE;
	trust = get_trust(ch);
	for (cmd = 0; cmd_table[cmd].name[0] != '\0'; cmd++)
	{
		if (command[0] == cmd_table[cmd].name[0] &&
			!str_prefix(command, cmd_table[cmd].name) &&
			cmd_table[cmd].level <= trust)
		{
			found = TRUE;
			break;
		}
	}

	/*
	 * Log and snoop.
	 */
	if (cmd_table[cmd].log == LOG_NEVER)
		strcpy(logline, "");

	if ((!IS_NPC(ch) && IS_SET(ch->act, PLR_LOG)) || fLogAll ||
		cmd_table[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)
	{
		write_to_buffer(ch->desc->snoop_by, "% ", 2);
		write_to_buffer(ch->desc->snoop_by, logline, 0);
		write_to_buffer(ch->desc->snoop_by, "\n\r", 2);
	}

	if (!found)
	{
		/*
		 * Look for command in socials table.
		 */
		if (!check_social(ch, command, argument))
			chprintln(ch, "Huh?");
		return;
	}
	else if (check_disabled(&cmd_table[cmd]))
	{
		chprintln(ch, "This command has been temporarily disabled.");
		return;
	}

	/*
	 * Character not in position for command?
	 */
	if (ch->position < cmd_table[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;

		}
		return;
	}

	/*
	 * Dispatch the command.
	 */
	(*cmd_table[cmd].do_fun) (ch, argument);

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

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

	found = FALSE;
	for (cmd = 0; social_table[cmd].name != NULL; cmd++)
	{
		if (command[0] == social_table[cmd].name[0] &&
			!str_prefix(command, social_table[cmd].name))
		{
			found = TRUE;
			break;
		}
	}

	if (!found)
		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(social_table[cmd].name, "snore"))
			break;
		chprintln(ch, "In your dreams, or what?");
		return TRUE;

	}

	one_argument(argument, arg);
	victim = NULL;
	if (arg[0] == '\0')
	{
		act(social_table[cmd].others_no_arg, ch, NULL, victim, TO_ROOM);
		act(social_table[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(social_table[cmd].others_auto, ch, NULL, victim, TO_ROOM);
		act(social_table[cmd].char_auto, ch, NULL, victim, TO_CHAR);
	}
	else
	{
		act(social_table[cmd].others_found, ch, NULL, victim, TO_NOTVICT);
		act(social_table[cmd].char_found, ch, NULL, victim, TO_CHAR);
		act(social_table[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(social_table[cmd].others_found, victim, NULL,
					ch, TO_NOTVICT);
				act(social_table[cmd].char_found, victim, NULL, ch, TO_CHAR);
				act(social_table[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)
{
	int cmd;
	int col;
	struct cmd_type *commands = NULL;

	alloc_mem(commands, struct cmd_type, maxCommands + 1);

	commands =
		(struct cmd_type *) memcpy(commands, cmd_table,
								   sizeof(struct cmd_type) * maxCommands + 1);

	qsort(commands, maxCommands, sizeof(cmd_table[0]), srt_cmds_name);

	col = 0;
	for (cmd = 0; cmd < maxCommands; cmd++)
	{
		if (commands[cmd].level < LEVEL_HERO &&
			commands[cmd].level <= get_trust(ch) && commands[cmd].show)
		{
			chprintf(ch, "%-12s", commands[cmd].name);
			if (++col % 6 == 0)
				chprintln(ch, "");
		}
	}

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

	free_mem(commands);
	return;
}

CH_CMD(do_wizhelp)
{
	int cmd;
	int col;
	struct cmd_type *commands = NULL;

	alloc_mem(commands, struct cmd_type, maxCommands + 1);

	commands =
		(struct cmd_type *) memcpy(commands, cmd_table,
								   sizeof(struct cmd_type) * maxCommands + 1);

	qsort(commands, maxCommands, sizeof(cmd_table[0]), srt_cmds_name);
	col = 0;
	for (cmd = 0; cmd < maxCommands; cmd++)
	{
		if (commands[cmd].level >= LEVEL_HERO &&
			commands[cmd].level <= get_trust(ch) && commands[cmd].show)
		{
			chprintf(ch, "%-12s", commands[cmd].name);
			if (++col % 6 == 0)
				chprintln(ch, "");
		}
	}

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

	free_mem(commands);
	return;
}

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

CH_CMD(do_disable)
{
	int i;
	DISABLED_DATA *p;

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

	if (!argument[0])			/* 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");

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

	/* command given */

	/* First check if it is one of the disabled commands */
	for (p = disabled_first; p; p = p->next)
		if (!str_cmp(argument, 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_mem(p);			/* free node */
		save_disabled();		/* save to disk */
		chprintln(ch, "Command enabled.");
	}
	else						/* not a disabled command, check if that command exists */
	{
		/* IQ test */
		if (!str_cmp(argument, "disable"))
		{
			chprintln(ch, "You cannot disable the disable command.");
			return;
		}

		/* Search for the command */
		for (i = 0; cmd_table[i].name[0] != '\0'; i++)
			if (!str_cmp(cmd_table[i].name, argument))
				break;

		/* Found? */
		if (cmd_table[i].name[0] == '\0')
		{
			chprintln(ch, "No such command.");
			return;
		}

		/* Can the imm use this command at all ? */
		if (cmd_table[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 = &cmd_table[i];
		p->disabled_by = str_dup(ch->name);	/* save name of disabler */
		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(const struct cmd_type * command)
{
	DISABLED_DATA *p;

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

	return FALSE;
}

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

	disabled_first = NULL;

	fp = file_open(DISABLED_FILE, "r");

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

	name = fread_word(fp);

	while (str_cmp(name, END_MARKER))	/* as long as name is NOT END_MARKER :) */
	{
		/* Find the command in the table */
		for (i = 0; cmd_table[i].name[0]; i++)
			if (!str_cmp(cmd_table[i].name, name))
				break;

		if (!cmd_table[i].name[0])	/* command does not exist? */
		{
			bug("Skipping uknown command in " DISABLED_FILE " file.", 0);
			fread_number(fp);	/* level */
			fread_word(fp);		/* disabled_by */
		}
		else					/* add new disabled command */
		{
			alloc_mem(p, DISABLED_DATA, 1);
			p->command = &cmd_table[i];
			p->level = fread_number(fp);
			p->disabled_by = str_dup(fread_word(fp));
			LINK(p, disabled_first, disabled_last, next, prev);
		}

		name = fread_word(fp);
	}

	file_close(fp);
}

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

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

	fp = file_open(DISABLED_FILE, "w");

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

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

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

	file_close(fp);
}