dxx-rebirth/similar/main/cmd.cpp

556 lines
11 KiB
C++
Raw Normal View History

2015-02-11 07:35:44 +00:00
/*
* This file is part of the DXX-Rebirth project <http://www.dxx-rebirth.com/>.
* It is copyright by its individual contributors, as recorded in the
* project's Git history. See COPYING.txt at the top level for license
* terms and a link to the Git history.
*
*/
/*
*
* Command parsing and processing
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dxxerror.h"
#include "u_mem.h"
#include "strutil.h"
#include "inferno.h"
#include "console.h"
2015-02-22 21:05:35 +00:00
#include "hash.h"
2015-02-11 07:35:44 +00:00
2015-02-24 04:42:34 +00:00
struct cmd_t
2015-02-11 07:35:44 +00:00
{
2015-02-12 06:34:18 +00:00
const char *name;
2015-02-11 07:35:44 +00:00
cmd_handler_t function;
2015-02-12 06:34:18 +00:00
const char *help_text;
2015-02-24 04:42:34 +00:00
};
2015-02-11 07:35:44 +00:00
2015-02-22 21:05:35 +00:00
#define CMD_MAX_CMDS 1024
2015-02-11 07:35:44 +00:00
/* The list of cmds */
2015-02-22 21:05:35 +00:00
static hashtable cmd_hash;
static cmd_t *cmd_list[CMD_MAX_CMDS];
static int Num_cmds;
2015-02-11 07:35:44 +00:00
#define ALIAS_NAME_MAX 32
2015-02-24 04:42:34 +00:00
struct cmd_alias_t
2015-02-11 07:35:44 +00:00
{
char name[ALIAS_NAME_MAX];
char *value;
2015-02-24 04:42:34 +00:00
};
2015-02-11 07:35:44 +00:00
2015-02-22 21:05:35 +00:00
#define CMD_MAX_ALIASES 1024
2015-02-11 07:35:44 +00:00
/* The list of aliases */
2015-02-22 21:05:35 +00:00
static hashtable cmd_alias_hash;
static cmd_alias_t *cmd_alias_list[CMD_MAX_ALIASES];
static int Num_cmd_aliases;
static cmd_t *cmd_findcommand(const char *cmd_name)
{
int i;
i = hashtable_search( &cmd_hash, cmd_name );
if ( i < 0 )
return NULL;
return cmd_list[i];
}
static cmd_alias_t *cmd_findalias(const char *alias_name )
{
int i;
i = hashtable_search( &cmd_alias_hash, alias_name );
if ( i < 0 )
return NULL;
return cmd_alias_list[i];
}
2015-02-11 07:35:44 +00:00
/* add a new console command */
2015-02-12 06:34:18 +00:00
void cmd_addcommand(const char *cmd_name, cmd_handler_t cmd_func, const char *cmd_help_text)
2015-02-11 07:35:44 +00:00
{
cmd_t *cmd;
Assert(cmd_name != NULL);
2015-02-22 21:05:35 +00:00
if (cmd_findcommand(cmd_name))
{
Int3();
con_printf(CON_NORMAL, "command %s already exists, not adding", cmd_name);
return;
2015-02-11 07:35:44 +00:00
}
2015-02-22 21:05:35 +00:00
/* create command, insert to hashtable */
2015-02-11 07:35:44 +00:00
MALLOC(cmd, cmd_t, 1);
cmd->name = cmd_name;
cmd->function = cmd_func;
2015-02-12 01:13:33 +00:00
cmd->help_text = cmd_help_text;
2015-02-22 21:05:35 +00:00
hashtable_insert(&cmd_hash, cmd_name, Num_cmds);
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_addcommand: added %s", cmd->name);
2015-02-22 21:05:35 +00:00
cmd_list[Num_cmds++] = cmd;
2015-02-11 07:35:44 +00:00
}
2015-02-24 04:42:34 +00:00
struct cmd_queue_t
2015-02-11 07:35:44 +00:00
{
char *command_line;
2015-02-24 04:42:34 +00:00
struct cmd_queue_t *next;
};
2015-02-11 07:35:44 +00:00
/* The list of commands to be executed */
2015-02-12 06:34:18 +00:00
static cmd_queue_t *cmd_queue_head;
static cmd_queue_t *cmd_queue_tail;
2015-02-11 07:35:44 +00:00
void cvar_cmd_set(int argc, char **argv);
/* execute a parsed command */
2015-04-25 19:56:19 +00:00
static void cmd_execute(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
cmd_t *cmd;
cmd_alias_t *alias;
2015-02-22 21:05:35 +00:00
if ( (cmd = cmd_findcommand(argv[0])) )
{
con_printf(CON_DEBUG, "cmd_execute: executing %s", argv[0]);
cmd->function(argc, argv);
return;
2015-02-11 07:35:44 +00:00
}
2015-02-22 21:05:35 +00:00
if ( (alias = cmd_findalias(argv[0])) && alias->value )
{
con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s", alias->name, alias->value);
cmd_insert(alias->value);
return;
2015-02-11 07:35:44 +00:00
}
/* Otherwise */
{ // set value of cvar
char *new_argv[argc+1];
int i;
new_argv[0] = (char *)("set");
for (i = 0; i < argc; i++)
new_argv[i+1] = argv[i];
cvar_cmd_set(argc + 1, new_argv);
}
}
/* Parse an input string */
2015-04-25 19:56:19 +00:00
static void cmd_parse(char *input)
2015-02-11 07:35:44 +00:00
{
char buffer[CMD_MAX_LENGTH];
char *tokens[CMD_MAX_TOKENS];
int num_tokens;
uint_fast32_t i, l;
2015-02-11 07:35:44 +00:00
Assert(input != NULL);
/* Strip leading spaces */
2015-02-12 06:34:18 +00:00
while( isspace(*input) ) { ++input; }
strncpy( buffer, input, sizeof(buffer) );
2015-02-12 06:34:18 +00:00
2015-02-11 07:35:44 +00:00
//printf("lead strip \"%s\"\n",buffer);
l = strlen(buffer);
2015-02-11 07:35:44 +00:00
/* If command is empty, give up */
if (l==0) return;
/* Strip trailing spaces */
for (i=l-1; i>0 && isspace(buffer[i]); i--) ;
buffer[i+1] = 0;
//printf("trail strip \"%s\"\n",buffer);
/* Split into tokens */
l = strlen(buffer);
2015-02-11 07:35:44 +00:00
num_tokens = 1;
tokens[0] = buffer;
for (i=1; i<l; i++) {
if (buffer[i] == '"') {
tokens[num_tokens - 1] = &buffer[++i];
while (i < l && buffer[i] != '"')
i++;
buffer[i] = 0;
continue;
}
if (isspace(buffer[i]) || buffer[i] == '=') {
buffer[i] = 0;
while (isspace(buffer[i+1]) && (i+1 < l)) i++;
tokens[num_tokens++] = &buffer[i+1];
}
}
/* Check for matching commands */
cmd_execute(num_tokens, tokens);
}
int cmd_queue_wait = 0;
int cmd_queue_process(void)
{
cmd_queue_t *cmd;
while (!cmd_queue_wait && cmd_queue_head) {
cmd = cmd_queue_head;
cmd_queue_head = cmd_queue_head->next;
if (!cmd_queue_head)
cmd_queue_tail = NULL;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_queue_process: processing %s", cmd->command_line);
2015-02-11 07:35:44 +00:00
cmd_parse(cmd->command_line); // Note, this may change the queue
d_free(cmd->command_line);
d_free(cmd);
}
if (cmd_queue_wait > 0) {
cmd_queue_wait--;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_queue_process: waiting");
2015-02-11 07:35:44 +00:00
return 1;
}
return 0;
}
/* execute until there are no commands left */
void cmd_queue_flush(void)
{
while (cmd_queue_process()) {
}
}
/* Add some commands to the queue to be executed */
2015-02-12 06:34:18 +00:00
void cmd_enqueue(int insert, const char *input)
2015-02-11 07:35:44 +00:00
{
cmd_queue_t *item, *head, *tail;
char output[CMD_MAX_LENGTH];
char *optr;
Assert(input != NULL);
head = tail = NULL;
while (*input) {
optr = output;
int quoted = 0;
/* Strip leading spaces */
while(isspace(*input) || *input == ';')
input++;
/* If command is empty, give up */
if (! *input)
continue;
/* Find the end of this line (\n, ;, or nul) */
do {
if (!*input)
break;
if (*input == '"') {
quoted = 1 - quoted;
continue;
} else if ( *input == '\n' || (!quoted && *input == ';') ) {
input++;
break;
}
} while ((*optr++ = *input++));
*optr = 0;
/* make a new queue item, add it to list */
MALLOC(item, cmd_queue_t, 1);
item->command_line = d_strdup(output);
item->next = NULL;
if (!head)
head = item;
if (tail)
tail->next = item;
tail = item;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_enqueue: adding %s", output);
2015-02-11 07:35:44 +00:00
}
if (insert) {
/* add our list to the head of the main list */
if (cmd_queue_head)
tail->next = cmd_queue_head;
if (!cmd_queue_tail)
cmd_queue_tail = tail;
cmd_queue_head = head;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_enqueue: added to front of list");
2015-02-11 07:35:44 +00:00
} else {
/* add our list to the tail of the main list */
if (!cmd_queue_head)
cmd_queue_head = head;
if (cmd_queue_tail)
cmd_queue_tail->next = head;
cmd_queue_tail = tail;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_enqueue: added to back of list");
2015-02-11 07:35:44 +00:00
}
}
2015-02-12 06:34:18 +00:00
void cmd_enqueuef(int insert, const char *fmt, ...)
2015-02-11 07:35:44 +00:00
{
va_list arglist;
char buf[CMD_MAX_LENGTH];
va_start (arglist, fmt);
vsnprintf(buf, sizeof(buf), fmt, arglist);
2015-02-11 07:35:44 +00:00
va_end (arglist);
cmd_enqueue(insert, buf);
}
/* Attempt to autocomplete an input string */
2015-02-12 06:34:18 +00:00
const char *cmd_complete(char *input)
2015-02-11 07:35:44 +00:00
{
2015-02-22 21:05:35 +00:00
int i;
uint_fast32_t len = strlen(input);
2015-02-11 07:35:44 +00:00
if (!len)
return NULL;
2015-02-22 21:05:35 +00:00
for (i = 0; i < Num_cmds; i++)
if (!d_strnicmp(input, cmd_list[i]->name, len))
return cmd_list[i]->name;
for (i = 0; i < Num_cmd_aliases; i++)
if (!d_strnicmp(input, cmd_alias_list[i]->name, len))
return cmd_alias_list[i]->name;
2015-02-11 07:35:44 +00:00
return cvar_complete(input);
}
/* alias */
2015-04-25 19:56:19 +00:00
static void cmd_alias(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
cmd_alias_t *alias;
char buf[CMD_MAX_LENGTH] = "";
int i;
2015-02-12 01:13:33 +00:00
2015-02-11 07:35:44 +00:00
if (argc < 2) {
2015-02-12 06:59:33 +00:00
con_printf(CON_NORMAL, "aliases:");
2015-02-22 21:05:35 +00:00
for (i = 0; i < Num_cmd_aliases; i++)
con_printf(CON_NORMAL, "%s: %s", cmd_alias_list[i]->name, cmd_alias_list[i]->value);
2015-02-11 07:35:44 +00:00
return;
}
if (argc == 2) {
2015-02-22 21:05:35 +00:00
if ( (alias = cmd_findalias(argv[1])) && alias->value )
{
con_printf(CON_NORMAL, "%s: %s", alias->name, alias->value);
return;
}
2015-02-12 06:59:33 +00:00
con_printf(CON_NORMAL, "alias: %s not found", argv[1]);
2015-02-11 07:35:44 +00:00
return;
}
for (i = 2; i < argc; i++) {
if (i > 2)
strncat(buf, " ", sizeof(buf) - strlen(buf) - 1);
strncat(buf, argv[i], sizeof(buf) - strlen(buf) - 1);
2015-02-11 07:35:44 +00:00
}
2015-02-22 21:05:35 +00:00
if ( (alias = cmd_findalias(argv[1])) )
{
if ( alias->value )
2015-02-11 07:35:44 +00:00
d_free(alias->value);
2015-02-22 21:05:35 +00:00
alias->value = d_strdup(buf);
return;
2015-02-11 07:35:44 +00:00
}
MALLOC(alias, cmd_alias_t, 1);
strncpy(alias->name, argv[1], sizeof(alias->name));
2015-02-11 07:35:44 +00:00
alias->value = d_strdup(buf);
2015-02-22 21:05:35 +00:00
hashtable_insert(&cmd_alias_hash, argv[1], Num_cmd_aliases);
cmd_alias_list[Num_cmd_aliases++] = alias;
2015-02-11 07:35:44 +00:00
}
/* unalias */
2015-04-25 19:56:19 +00:00
static void cmd_unalias(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
2015-02-22 21:05:35 +00:00
cmd_alias_t *alias;
2015-02-12 01:13:33 +00:00
if (argc < 2 || argc > 2) {
cmd_insertf("help %s", argv[0]);
2015-02-11 07:35:44 +00:00
return;
}
2015-02-12 01:13:33 +00:00
2015-02-22 21:05:35 +00:00
alias = cmd_findalias(argv[1]);
if (!alias || !alias->value )
{
2015-02-12 06:59:33 +00:00
con_printf(CON_NORMAL, "unalias: %s not found", argv[1]);
2015-02-11 07:35:44 +00:00
return;
}
2015-02-22 21:05:35 +00:00
2015-02-11 07:35:44 +00:00
d_free(alias->value);
2015-02-22 21:05:35 +00:00
//d_free(alias); // Can't remove from hashtable, so just leave it
2015-02-11 07:35:44 +00:00
}
/* echo to console */
2015-04-25 19:56:19 +00:00
static void cmd_echo(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
char buf[CMD_MAX_LENGTH] = "";
int i;
2015-02-12 01:13:33 +00:00
2015-02-11 07:35:44 +00:00
for (i = 1; i < argc; i++) {
if (i > 1)
strncat(buf, " ", sizeof(buf) - strlen(buf) - 1);
strncat(buf, argv[i], sizeof(buf) - strlen(buf) - 1);
2015-02-11 07:35:44 +00:00
}
2015-04-25 20:20:39 +00:00
con_puts(CON_NORMAL, buf);
2015-02-11 07:35:44 +00:00
}
/* execute script */
2015-04-25 19:56:19 +00:00
static void cmd_exec(int argc, char **argv) {
2015-02-11 07:35:44 +00:00
cmd_queue_t *item, *head, *tail;
PHYSFSX_gets_line_t<CMD_MAX_LENGTH> line;
2015-02-12 01:13:33 +00:00
if (argc < 2 || argc > 2) {
cmd_insertf("help %s", argv[0]);
2015-02-11 07:35:44 +00:00
return;
}
head = tail = NULL;
auto f = PHYSFSX_openReadBuffered(argv[1]);
if (!f) {
2015-02-12 06:59:33 +00:00
con_printf(CON_CRITICAL, "exec: %s not found", argv[1]);
2015-02-11 07:35:44 +00:00
return;
}
while (PHYSFSX_fgets(line, f)) {
/* make a new queue item, add it to list */
MALLOC(item, cmd_queue_t, 1);
item->command_line = d_strdup(line);
item->next = NULL;
if (!head)
head = item;
if (tail)
tail->next = item;
tail = item;
2015-02-12 06:34:18 +00:00
con_printf(CON_DEBUG, "cmd_exec: adding %s", static_cast<const char *>(line));
2015-02-11 07:35:44 +00:00
}
/* add our list to the head of the main list */
if (cmd_queue_head)
tail->next = cmd_queue_head;
if (!cmd_queue_tail)
cmd_queue_tail = tail;
cmd_queue_head = head;
2015-02-12 06:59:33 +00:00
con_printf(CON_DEBUG, "cmd_exec: added to front of list");
2015-02-11 07:35:44 +00:00
}
/* get help */
2015-04-25 19:56:19 +00:00
static void cmd_help(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
cmd_t *cmd;
2015-02-12 01:13:33 +00:00
if (argc > 2) {
cmd_insertf("help %s", argv[0]);
2015-02-11 07:35:44 +00:00
return;
}
if (argc < 2) {
2015-02-22 21:05:35 +00:00
int i;
2015-02-12 06:59:33 +00:00
con_printf(CON_NORMAL, "Available commands:");
2015-02-22 21:05:35 +00:00
for (i = 0; i < Num_cmds; i++)
con_printf(CON_NORMAL, " %s", cmd_list[i]->name);
2015-02-11 07:35:44 +00:00
return;
}
2015-02-12 01:13:33 +00:00
2015-02-22 21:05:35 +00:00
cmd = cmd_findcommand(argv[1]);
2015-02-12 01:13:33 +00:00
if (!cmd) {
2015-02-12 06:59:33 +00:00
con_printf(CON_URGENT, "Command %s not found", argv[1]);
2015-02-12 01:13:33 +00:00
return;
}
if (!cmd->help_text) {
2015-02-12 06:59:33 +00:00
con_printf(CON_NORMAL, "%s: no help found", argv[1]);
2015-02-12 01:13:33 +00:00
return;
}
2015-04-25 20:20:39 +00:00
con_puts(CON_NORMAL, cmd->help_text);
2015-02-11 07:35:44 +00:00
}
/* execute script */
2015-04-25 19:56:19 +00:00
static void cmd_wait(int argc, char **argv)
2015-02-11 07:35:44 +00:00
{
2015-02-12 01:13:33 +00:00
if (argc > 2) {
cmd_insertf("help %s", argv[0]);
2015-02-11 07:35:44 +00:00
return;
}
if (argc < 2)
cmd_queue_wait = 1;
else
cmd_queue_wait = atoi(argv[1]);
}
2015-04-25 19:56:19 +00:00
static void cmd_free(void)
2015-02-11 07:35:44 +00:00
{
2015-02-22 21:05:35 +00:00
while (Num_cmds--)
d_free(cmd_list[Num_cmds]);
while (Num_cmd_aliases--)
{
if (cmd_alias_list[Num_cmd_aliases]->value)
d_free(cmd_alias_list[Num_cmd_aliases]->value);
d_free(cmd_alias_list[Num_cmd_aliases]);
2015-02-11 07:35:44 +00:00
}
}
void cmd_init(void)
{
2015-02-12 01:13:33 +00:00
cmd_addcommand("alias", cmd_alias, "alias <name> <commands>\n" " define <name> as an alias for <commands>\n"
"alias <name>\n" " show the current definition of <name>\n"
2015-02-12 06:59:33 +00:00
"alias\n" " show all defined aliases");
cmd_addcommand("unalias", cmd_unalias, "unalias <name>\n" " undefine the alias <name>");
cmd_addcommand("echo", cmd_echo, "echo [text]\n" " write <text> to the console");
cmd_addcommand("exec", cmd_exec, "exec <file>\n" " execute <file>");
cmd_addcommand("help", cmd_help, "help [command]\n" " get help for <command>, or list all commands if not specified.");
cmd_addcommand("wait", cmd_wait, "usage: wait [n]\n" " stop processing commands, resume in <n> cycles (default 1)");
2015-02-12 01:13:33 +00:00
2015-02-11 07:35:44 +00:00
atexit(cmd_free);
}