/* * This file is part of the DXX-Rebirth project . * 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 #include #include #include #include #include #include #include "dxxerror.h" #include "u_mem.h" #include "strutil.h" #include "inferno.h" #include "cmd.h" #include "console.h" #include "cvar.h" #include "physfsx.h" #include "compiler-range_for.h" #include namespace { struct cmd_t { const char *name; cmd_handler_t function; const char *help_text; }; /* The list of cmds */ static std::map> cmd_list; #define ALIAS_NAME_MAX 32 struct cmd_alias_t { char name[ALIAS_NAME_MAX]; RAIIdmem value; }; } #define CMD_MAX_ALIASES 1024 /* The list of aliases */ static std::map> cmd_alias_list; static cmd_t *cmd_findcommand(const char *cmd_name) { const auto i = cmd_list.find(cmd_name); return i == cmd_list.end() ? nullptr : i->second.get(); } static cmd_alias_t *cmd_findalias(const char *alias_name ) { const auto i = cmd_alias_list.find(alias_name); return i == cmd_alias_list.end() ? nullptr : i->second.get(); } /* add a new console command */ void cmd_addcommand(const char *cmd_name, cmd_handler_t cmd_func, const char *cmd_help_text) { const auto i = cmd_list.insert(std::make_pair(cmd_name, std::unique_ptr{})); if (!i.second) { Int3(); con_printf(CON_NORMAL, "command %s already exists, not adding", cmd_name); return; } auto cmd = (i.first->second = std::make_unique()).get(); /* create command, insert to hashtable */ cmd->name = cmd_name; cmd->function = cmd_func; cmd->help_text = cmd_help_text; con_printf(CON_DEBUG, "cmd_addcommand: added %s", cmd->name); } namespace { struct cmd_queue_t { RAIIdmem command_line; explicit cmd_queue_t(char *p) : command_line(p) { } }; } /* The list of commands to be executed */ static std::forward_list cmd_queue; /* execute a parsed command */ static void cmd_execute(unsigned long argc, const char *const *const argv) { cmd_t *cmd; cmd_alias_t *alias; if ( (cmd = cmd_findcommand(argv[0])) ) { con_printf(CON_DEBUG, "cmd_execute: executing %s", argv[0]); cmd->function(argc, argv); return; } if ( (alias = cmd_findalias(argv[0])) && alias->value ) { con_printf(CON_DEBUG, "cmd_execute: pushing alias \"%s\": %s", alias->name, alias->value.get()); cmd_insert(alias->value.get()); return; } /* Otherwise */ if (argc < 31) { // set value of cvar const char *new_argv[32]; int i; new_argv[0] = "set"; for (i = 0; i < argc; i++) new_argv[i+1] = argv[i]; cvar_cmd_set(argc + 1, new_argv); } } /* Parse an input string */ static void cmd_parse(char *input) { char buffer[CMD_MAX_LENGTH]; char *tokens[CMD_MAX_TOKENS]; int num_tokens; uint_fast32_t i, l; Assert(input != NULL); /* Strip leading spaces */ while( isspace(*input) ) { ++input; } buffer[sizeof(buffer) - 1] = 0; strncpy(buffer, input, sizeof(buffer) - 1); //printf("lead strip \"%s\"\n",buffer); l = strlen(buffer); /* 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); num_tokens = 1; tokens[0] = buffer; for (i=1; ivalue.get()); return; } if (argc == 2) { cmd_alias_t *alias; if ( (alias = cmd_findalias(argv[1])) && alias->value ) { con_printf(CON_NORMAL, "%s: %s", alias->name, alias->value.get()); return; } con_printf(CON_NORMAL, "alias: %s not found", argv[1]); return; } for (int i = 2; i < argc; i++) { if (i > 2) strncat(buf, " ", sizeof(buf) - strlen(buf) - 1); strncat(buf, argv[i], sizeof(buf) - strlen(buf) - 1); } const auto i = cmd_alias_list.insert(std::make_pair(argv[1], std::unique_ptr{})); auto alias = i.first->second.get(); if (i.second) { alias = (i.first->second = std::make_unique()).get(); alias->name[sizeof(alias->name) - 1] = 0; strncpy(alias->name, argv[1], sizeof(alias->name) - 1); } alias->value.reset(d_strdup(buf)); } /* unalias */ static void cmd_unalias(unsigned long argc, const char *const *const argv) { if (argc < 2 || argc > 2) { cmd_insertf("help %s", argv[0]); return; } const char *alias_name = argv[1]; const auto alias = cmd_alias_list.find(alias_name); if (alias == cmd_alias_list.end()) { con_printf(CON_NORMAL, "unalias: %s not found", alias_name); return; } cmd_alias_list.erase(alias); } /* echo to console */ static void cmd_echo(unsigned long argc, const char *const *const argv) { char buf[CMD_MAX_LENGTH] = ""; int i; 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); } con_puts(CON_NORMAL, buf); } /* execute script */ static void cmd_exec(unsigned long argc, const char *const *const argv) { PHYSFSX_gets_line_t line; if (argc < 2 || argc > 2) { cmd_insertf("help %s", argv[0]); return; } auto &&[f, physfserr] = PHYSFSX_openReadBuffered(argv[1]); if (!f) { con_printf(CON_CRITICAL, "exec: failed to open \"%s\": %s", argv[1], PHYSFS_getErrorByCode(physfserr)); return; } std::forward_list l; auto i = l.before_begin(); while (PHYSFSX_fgets(line, f)) { /* make a new queue item, add it to list */ i = l.emplace_after(i, cmd_queue_t{d_strdup(line)}); con_printf(CON_DEBUG, "cmd_exec: adding %s", static_cast(line)); } /* add our list to the head of the main list */ con_puts(CON_DEBUG, "cmd_exec: added to front of list"); cmd_queue.splice_after(cmd_queue.before_begin(), std::move(l)); } /* get help */ static void cmd_help(unsigned long argc, const char *const *const argv) { cmd_t *cmd; if (argc > 2) { cmd_insertf("help %s", argv[0]); return; } if (argc < 2) { con_puts(CON_NORMAL, "Available commands:"); range_for (const auto &i, cmd_list) con_printf(CON_NORMAL, " %s", i.first); return; } cmd = cmd_findcommand(argv[1]); if (!cmd) { con_printf(CON_URGENT, "Command %s not found", argv[1]); return; } if (!cmd->help_text) { con_printf(CON_NORMAL, "%s: no help found", argv[1]); return; } con_puts(CON_NORMAL, cmd->help_text); } /* execute script */ static void cmd_wait(unsigned long argc, const char *const *const argv) { if (argc > 2) { cmd_insertf("help %s", argv[0]); return; } if (argc < 2) cmd_queue_wait = 1; else cmd_queue_wait = atoi(argv[1]); } void cmd_init(void) { cmd_addcommand("alias", cmd_alias, "alias \n" " define as an alias for \n" "alias \n" " show the current definition of \n" "alias\n" " show all defined aliases"); cmd_addcommand("unalias", cmd_unalias, "unalias \n" " undefine the alias "); cmd_addcommand("echo", cmd_echo, "echo [text]\n" " write to the console"); cmd_addcommand("exec", cmd_exec, "exec \n" " execute "); cmd_addcommand("help", cmd_help, "help [command]\n" " get help for , or list all commands if not specified."); cmd_addcommand("wait", cmd_wait, "usage: wait [n]\n" " stop processing commands, resume in cycles (default 1)"); }