/*
* handle_cmd.c -- handling commands to PMF (local and remote)
*
* PMF -- Padrone's MudFrontend, a frontend for (maybe mostly LP-)mud
* Thomas Padron-McCarthy (Email: padrone@lysator.liu.se), 1990, 1991
* Share and enjoy, but be nice: don't steal my program! Hugo is watching!
* This file latest updated: Sept 22, 1991
*
*/
#include <strings.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include "safe_malloc.h"
#include "config.h"
#include "pmf.h"
#include "globals.h"
extern char *get_input_line(), *expand_alias(),
*find_alias_string(),
*expand_history(), *get_statstring(), *getmud(),
*apply_definition();
extern int handle_command();
extern void do_remote_command();
/*---------------------------------------------------------------------------*/
/* This function takes care of a command given by the player.
* It is supposed to be in a malloc'ed string,
* with the trailing '\n' removed.
* It will not be free'd in this function or in functions called by it.
*/
void player_command(command_line)
char *command_line;
{
char *cp, *cmd_subst = NULL;
int retval;
USER_DEBUG(("player_command(\"%s\")", command_line));
cmd_subst = NULL;
/* First, skip leading white space. */
cp = command_line;
while (isspace(*cp))
++cp;
/* We don't check for comment lines here, since it's only when
* reading commands from files that lines starting with #
* should be skipped.
*/
/* Blank lines are simply sent to the Mud process.
* All the white space is sent.
*/
if (*cp == 0) {
do_remote_command(command_line, 0);
return;
}
/* If this is a "quote" command, simply send the rest of the line to Mud. */
if ( (slash_commands && !strncmp("/quote", cp, 6))
|| (!slash_commands && !strncmp("quote", cp, 5))) {
do_remote_command(cp + (slash_commands ? 7 : 6), 0);
add_mud_history(cp);
return;
}
if (substitute_history) {
/* Not a quote command. Substitue any history references (!! and !number). */
cmd_subst = expand_history(cp);
if (cmd_subst) {
/* The "cmd_subst" string is a new, malloc'ed, string.
* Don't forget to free it later!
*/
USER_DEBUG(("History substitution yielded: %s", cmd_subst));
cp = cmd_subst;
}
if (cmd_subst && verbose)
ldisplay("History substitution yielded: %s\n", cp);
}
add_mud_history(cp);
/* History substitution might have yielded a new "quote" command,
* but that will be handled in handle_command.
*/
if (cmd_subst)
retval = handle_command(cp, 0);
else
retval = handle_command(command_line, 0);
if (cmd_subst)
safe_free(cmd_subst);
if (retval && pmf_prompt && !sourcing)
ldisplay(pmf_prompt, query_latest_event_nr() + 1);
} /* player_command */
/* Handle a command line from the user (possibly form a source'd file).
* It is called AFTER any history substitution, and AFTER storing the
* command on the history list. Also, comments and "quote" commands have
* already been taken care of.
* If the line is expanded to several lines, by use of the $n notation,
* this function is called recursively for each of those new lines.
* If expressflag is true, the remote command(s) will be added at
* the HEAD of the queue of commands to send to the game
* (but not directly -- they are put on a special express list first).
* The number of executed local commands is returned.
*/
int handle_command(command_line, expressflag)
char *command_line;
int expressflag;
{
char *cp, *the_definition, *this_expanded, *latest_expanded;
int nr_words, nr_alias_expansions, i, result;
char *command_line_words[MAX_WORDS_PER_LINE],
command_line_words_buffer[2 * MAX_LINE_LENGTH];
int use_command_line, recursive_retval;
int was_slash_command;
USER_DEBUG(("handle_command(\"%s\", %d)", command_line, expressflag));
use_command_line = 1;
/* First, skip leading white space. It will still be there! */
cp = command_line;
while (isspace(*cp))
++cp;
/* Comment lines, i. e. lines that start with # (even after white space) are ignored.
* If this was a player command, those lines are skipped, but not if it was source'd.
*/
if (*cp == '#') {
return 0;
}
/* Blank lines are simply sent to the Mud process. All the white space is sent. */
if (*cp == 0) {
do_remote_command(command_line, expressflag);
return 0;
}
was_slash_command = slash_commands && *cp == '/';
/* If this is a "quote" command, simply send the rest of the line to Mud. */
if ( (slash_commands && !strncmp("/quote", cp, 6))
|| (!slash_commands && !strncmp("quote", cp, 5))) {
do_remote_command(cp + (slash_commands ? 7 : 6), 0);
return 0;
}
/* Now, do alias substitution! The function "expand_one_alias" will return NULL
* if no substitutions were done, or a malloc'ed string that we must free later!
* "cp" still points to the first non-blank char, so we can do "alias /foo fum".
*/
nr_alias_expansions = 0;
latest_expanded = NULL;
nr_words = word_split(cp, command_line_words, command_line_words_buffer);
/* If word_split failed (it returned 0),
* we simply send the line to the Mud process.
*/
if (nr_words == 0) {
do_remote_command(command_line, expressflag);
if (verbose)
ldisplay("The line is not interpreted by PMF, but is sent directly to MUD.\n");
return 0;
}
/* If the command line contained at least one un-quoted $n, it was
* split into lines instead of words. Each line is a new command!
*/
if (nr_words < 0) {
use_command_line = 0;
nr_words = -nr_words;
if (verbose) {
ldisplay("The command line was split into %d new command lines.\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
if (debug) {
int i;
ldisplay("DEBUG: The line was split (1) at $n into %d new lines:\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
recursive_retval = 0;
for (i=0; i<nr_words; ++i)
recursive_retval += handle_command(command_line_words[i], expressflag);
return recursive_retval;
}
if (debug && nr_words > 0) {
int i;
ldisplay("DEBUG: First splitting (1) of the command line: %d words:\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
while ((the_definition = find_alias_string(command_line_words[0])) != NULL) {
use_command_line = 0;
if (++nr_alias_expansions > MAX_ALIAS_EXPANSIONS)
error("Alias loop. Too many levels (%d) substituted.",
nr_alias_expansions);
this_expanded = apply_definition(the_definition, nr_words,
command_line_words, 1);
USER_DEBUG(("This alias substitution yielded: %s", this_expanded));
if (latest_expanded)
safe_free(latest_expanded);
latest_expanded = this_expanded;
cp = this_expanded;
nr_words = word_split(cp, command_line_words, command_line_words_buffer);
/* If word_split failed (it returned 0),
* we simply send the line to the Mud process.
*/
if (nr_words == 0) {
do_remote_command(cp, expressflag); /* This alias! */
if (verbose)
ldisplay("The line is not interpreted by PMF, but is sent directly to MUD.\n");
return 0;
}
/* If the command line contained at least one un-quoted $n, it was
* split into lines instead of words. Each line is a new command!
*/
if (nr_words < 0) {
nr_words = -nr_words;
if (verbose) {
ldisplay("The command line was split into %d new command lines:\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
if (debug) {
int i;
ldisplay("DEBUG: The line was split (2) at $n into %d new lines:\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
recursive_retval = 0;
for (i=0; i<nr_words; ++i)
recursive_retval += handle_command(command_line_words[i], expressflag);
return recursive_retval;
}
if (debug && nr_words > 0) {
int i;
ldisplay("DEBUG: This splitting (2) of the command line: %d words:\n", nr_words);
for (i=0; i<nr_words; ++i)
ldisplay(" %d: '%s'\n", i, command_line_words[i]);
}
} /* While it's still an alias. */
if (latest_expanded && verbose && !sourcing)
ldisplay("Alias substitution yielded: %s\n", cp);
for (i = nr_words; i < MAX_WORDS_PER_LINE; ++i)
command_line_words[i] = NULL;
ASSERT(!isspace(*cp));
/* Can be a local OR a remote command */
result = do_local_command(cp, nr_words, command_line_words, expressflag);
if (result == -1) {
/* No such local PMF command */
if (slash_commands && *cp == '/') {
message("No such local PMF command: \"%s\".", command_line_words[0]);
ldisplay(" The line is sent to MUD instead. Try \"/help /quote\"!\n");
}
if (use_command_line)
do_remote_command(command_line, expressflag);
else
do_remote_command(cp, expressflag);
}
if (latest_expanded)
safe_free(latest_expanded);
if (result < 0)
return 0;
else
return result;
} /* handle_command */
handle_the_player()
{
char *player_line;
while ((player_line = get_input_line(stdin)) != NULL)
player_command(player_line);
} /* handle_the_player */
/* Do the local command "the_local_command".
* 0 is returned if a local command was executed, even if it failed
* or was syntactically incorrect (wrong number of arguments).
* The expressflag is used for remote commands.
* 1 is returned if a local command and a remote command was executed.
* -1 is returned if "the_local_command" wasn't a valid local command.
*/
int do_local_command(the_local_command, nr_words, words, expressflag)
char *the_local_command, *words[];
int nr_words;
int expressflag;
{
char *restargs;
#define LOCAL_COMMAND(cmd, slashcmd) (slash_commands ? slashcmd : cmd)
if (!strcmp(words[0], LOCAL_COMMAND("quote", "/quote"))) {
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the word "quote". */
restargs += 5;
/* Now we are immediately after the word "quote"! */
if (isspace(*restargs))
++restargs;
do_remote_command(restargs, expressflag);
}
else if (!strcmp(words[0], LOCAL_COMMAND("connect", "/connect"))) {
if (nr_words > 3)
message("Too many arguments to the \"connect\" command.");
else
cmd_connect(words[1], words[2]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("disconnect", "/disconnect"))) {
if (nr_words > 1)
message("You cannot give any arguments to the \"disconnect\" command.");
else
cmd_disconnect();
}
else if (!strcmp(words[0], LOCAL_COMMAND("help", "/help"))) {
if (nr_words > 2)
message("Usage: /help [ SUBJECT ].");
else
cmd_help(words[1]);
}
else if (!strcmp(words[0], "help")) {
if (nr_words == 2 && (!strcmp(lower_string(words[1]), "pmf") || !strcmp(words[1], "front")))
cmd_help(0);
else {
if (verbose) {
ldisplay("(Note: PMF is passing on this \"help\" command to MUD.\n");
ldisplay(" Use the command \"/help\" for help about the PMF program.)\n");
}
do_remote_command(the_local_command, expressflag);
return 1;
}
}
else if (!strcmp(words[0], LOCAL_COMMAND("status", "/status"))) {
if (nr_words > 1)
message("You cannot give any arguments to the \"/status\" command.");
else
cmd_status();
}
else if (!strcmp(words[0], LOCAL_COMMAND("history", "/history"))) {
if (nr_words > 2)
message("Too many arguments to the \"history\" command.");
else
cmd_history(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("log", "/log"))) {
if (nr_words > 2)
message("Usage: /log [ FILENAME ]\n");
else
cmd_log(words[1], 0);
}
else if (!strcmp(words[0], LOCAL_COMMAND("debuglog", "/debuglog"))) {
if (nr_words > 2)
message("Usage: /debuglog [ FILENAME ]\n");
else
cmd_log(words[1], 1);
}
else if (!strcmp(words[0], LOCAL_COMMAND("perform", "/perform"))) {
if (nr_words < 2)
message("You must give a MUD command as argument to the \"perform\" command.");
else if (nr_words > 3)
message("Too many arguments to the \"perform\" command.");
else
cmd_perform(words[1], words[2]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("send", "/send"))) {
if (nr_words < 2)
message("You must give a file name as argument to the \"send\" command.");
else if (nr_words > 3)
message("Too many arguments to the \"send\" command.");
else
cmd_send(words[1], words[2], 0);
}
else if (!strcmp(words[0], LOCAL_COMMAND("receive", "/receive"))) {
if (nr_words < 2)
message("You must give a file name as argument to the \"receive\" command.");
else if (nr_words < 3)
message("You must give a stop string as second argument to the \"receive\" command.");
else if (nr_words > 3)
message("Too many arguments to the \"receive\" command.");
else
cmd_receive(words[1], words[2], 0);
}
else if (!strcmp(words[0], LOCAL_COMMAND("putfile", "/putfile"))) {
if (nr_words < 2)
message("You must give at least one file name as argument to \"putfile\".");
else if (nr_words > 3)
message("Too many arguments to \"putfile\".");
else
cmd_putfile(words[1], words[2]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("getfile", "/getfile"))) {
if (nr_words < 2)
message("You must give at least one file name as argument to \"getfile\".");
else if (nr_words > 4)
message("Too many arguments to \"getfile\".");
else
cmd_getfile(words[1], words[2]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("last", "/last"))) {
if (nr_words > 2)
message("Too many arguments to the \"last\" command.");
else
cmd_last(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("source", "/source"))) {
if (nr_words < 2)
message("You must give a file name as argument to the \"source\" command.");
else if (nr_words > 2)
message("Too many arguments to \"source\" command.");
else
cmd_source(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("dump", "/dump"))) {
if (nr_words < 2)
message("You must give a file name as argument to the \"dump\" command.");
else if (nr_words > 2)
message("Too many arguments to the \"dump\" command.");
else
cmd_dump(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("alias", "/alias"))) {
if (nr_words == 3)
restargs = words[2];
else if (nr_words > 3 && quotes_in_line(the_local_command)) {
message("No quoting allowed if you have more than two arguments to \"alias\".");
return 0;
}
else if (nr_words > 3) {
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the third word. */
}
else
restargs = NULL;
cmd_alias(words[1], restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("unalias", "/unalias"))) {
if (nr_words < 2)
message("You must give an argument to the \"unalias\" command.");
else if (nr_words > 2)
message("Too many arguments to the \"unalias\" command.");
else
cmd_unalias(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("action", "/action"))) {
if (nr_words == 3)
restargs = words[2];
else if (nr_words > 3 && quotes_in_line(the_local_command)) {
message("No quoting allowed if you have more than two arguments to \"action\".");
return 0;
}
else if (nr_words > 3) {
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the third word. */
}
else
restargs = NULL;
cmd_action(words[1], restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("unaction", "/unaction"))) {
if (nr_words < 2)
message("You must give an argument to the \"unaction\" command.");
else if (nr_words > 2)
message("Too many arguments to the \"unaction\" command.");
else
cmd_unaction(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("unactionall", "/unactionall"))) {
if (nr_words != 1)
message("You can't give any arguments to \"unactionall\".");
else
cmd_unactionall();
}
else if (!strcmp(words[0], LOCAL_COMMAND("sound", "/sound"))) {
if (nr_words == 3)
restargs = words[2];
else if (nr_words > 3 && quotes_in_line(the_local_command)) {
message("No quoting allowed if you have more than two arguments to \"sound\".");
return 0;
}
else if (nr_words > 3) {
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the third word. */
}
else
restargs = NULL;
#ifdef SOUND
cmd_sound(words[1], restargs);
#endif
}
else if (!strcmp(words[0], LOCAL_COMMAND("unsound", "/unsound"))) {
if (nr_words < 2)
message("You must give an argument to the \"unsound\" command.");
else if (nr_words > 2)
message("Too many arguments to the \"unsound\" command.");
#ifdef SOUND
else
cmd_unsound(words[1]);
#endif
}
else if (!strcmp(words[0], LOCAL_COMMAND("beep", "/beep"))) {
if (nr_words != 1)
message("You can not give any arguments to the \"beep\" command.");
else
cmd_beep();
}
else if (!strcmp(words[0], LOCAL_COMMAND("echo", "/echo"))) {
if (nr_words == 1)
restargs = NULL;
else if (nr_words == 2)
restargs = words[1];
else {
ASSERT(nr_words > 2);
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
}
cmd_echo(restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("cd", "/cd"))) {
if (nr_words > 2)
message("Usage: /cd [ DIRECTORY-NAME ]");
else
cmd_cd(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("cryptsay", "/cryptsay"))) {
if (nr_words == 1) {
message("You must give an argument to the \"cryptsay\" command.");
return 0;
}
else if (nr_words == 2)
restargs = words[1];
else {
ASSERT(nr_words > 2);
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
}
cmd_cryptsay(restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("crypttell", "/crypttell"))) {
if (nr_words < 3) {
message("You must give at least two arguments to the \"crypttell\" command.");
return 0;
}
else if (nr_words == 3)
restargs = words[2];
else {
ASSERT(nr_words > 3);
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
while (!isspace(*restargs))
++restargs;
/* Now we are just after the second word. */
++restargs;
/* Now we are after the second word, plus one space or tab. */
}
cmd_crypttell(words[1], restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("system", "/system"))) {
if (nr_words < 2)
restargs = NULL;
else if (nr_words == 2)
restargs = words[1];
else if (nr_words > 2 && quotes_in_line(the_local_command)) {
message("No quoting allowed if you have more than two arguments to \"system\".");
return 0;
}
else {
ASSERT(nr_words > 2);
restargs = the_local_command;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the first word. */
while (!isspace(*restargs))
++restargs;
while (isspace(*restargs))
++restargs;
/* Now we are at the beginning of the second word. */
}
cmd_system(restargs);
}
else if (!strcmp(words[0], LOCAL_COMMAND("gag", "/gag"))) {
if (nr_words > 2)
message("You cannot give more than one person or pattern as argument to the \"gag\" command.");
else
cmd_gag(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("ungag", "/ungag"))) {
if (nr_words < 2)
message("Yes, but who dou you want to ungag?");
else if (nr_words > 2)
message("You cannot give more than one person as argument to the \"ungag\" command.");
else
cmd_ungag(words[1]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("set", "/set"))) {
if (nr_words > 3)
message("Usage: /set VARIABLE-NAME [ VALUE ]");
else
cmd_set(words[1], words[2]);
}
else if (!strcmp(words[0], LOCAL_COMMAND("unset", "/unset"))) {
if (nr_words != 2)
message("Usage: /unset VARIABLE-NAME");
else {
cmd_unset(words[1]);
}
}
else if (!strcmp(words[0], LOCAL_COMMAND("quit", "/quit"))) {
if (nr_words > 1)
message("You cannot give any arguments to the command \"/quit\".");
else
cmd_quit();
}
else
return -1;
return 0;
} /* do_local_command */
void do_remote_command(the_remote_command, expressflag)
char *the_remote_command;
int expressflag;
{
if (!connected)
message("You cannot give commands to MUD until you are connected to MUD.");
else if (sending)
message("You cannot give commands to MUD while sending a file.");
else if (expressflag)
express_queue_mudline(the_remote_command, "");
else
queue_mudline(the_remote_command, "");
} /* do_remote_command */
syntax highlighted by Code2HTML, v. 0.9.1