diff options
Diffstat (limited to 'src/engine/e_console.cpp')
| -rw-r--r-- | src/engine/e_console.cpp | 464 |
1 files changed, 464 insertions, 0 deletions
diff --git a/src/engine/e_console.cpp b/src/engine/e_console.cpp new file mode 100644 index 00000000..c641289d --- /dev/null +++ b/src/engine/e_console.cpp @@ -0,0 +1,464 @@ +#include <base/system.h> +#include "e_if_other.h" +#include "e_console.h" +#include "e_config.h" +#include "e_engine.h" +#include "e_linereader.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + + +#define CONSOLE_MAX_STR_LENGTH 1024 +/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */ +#define MAX_PARTS (CONSOLE_MAX_STR_LENGTH+1)/2 + +typedef struct +{ + char string_storage[CONSOLE_MAX_STR_LENGTH+1]; + char *args_start; + + const char *command; + const char *args[MAX_PARTS]; + unsigned int num_args; +} PARSE_RESULT; + +static char *str_skipblanks(char *str) +{ + while(*str && (*str == ' ' || *str == '\t' || *str == '\n')) + str++; + return str; +} + +static char *str_skiptoblank(char *str) +{ + while(*str && (*str != ' ' && *str != '\t' && *str != '\n')) + str++; + return str; +} + +/* static int digit(char c) { return '0' <= c && c <= '9'; } */ + +static int console_parse_start(PARSE_RESULT *result, const char *string, int length) +{ + char *str; + int len = sizeof(result->string_storage); + if(length < len) + len = length; + + str_copy(result->string_storage, string, length); + str = result->string_storage; + + /* get command */ + str = str_skipblanks(str); + result->command = str; + str = str_skiptoblank(str); + + if(*str) + { + str[0] = 0; + str++; + } + + result->args_start = str; + result->num_args = 0; + return 0; +} + +static int console_parse_args(PARSE_RESULT *result, const char *format) +{ + char command; + char *str; + int optional = 0; + int error = 0; + + str = result->args_start; + + while(1) + { + /* fetch command */ + command = *format; + format++; + + if(!command) + break; + + if(command == '?') + optional = 1; + else + { + str = str_skipblanks(str); + + if(!(*str)) /* error, non optional command needs value */ + { + if(!optional) + error = 1; + break; + } + + /* add token */ + if(*str == '"') + { + char *dst; + str++; + result->args[result->num_args++] = str; + + dst = str; /* we might have to process escape data */ + while(1) + { + if(str[0] == '"') + break; + else if(str[0] == '\\') + { + if(str[1] == '\\') + str++; /* skip due to escape */ + else if(str[1] == '"') + str++; /* skip due to escape */ + } + else if(str[0] == 0) + return 1; /* return error */ + + *dst = *str; + dst++; + str++; + } + + /* write null termination */ + *dst = 0; + } + else + { + result->args[result->num_args++] = str; + + if(command == 'r') /* rest of the string */ + break; + else if(command == 'i') /* validate int */ + str = str_skiptoblank(str); + else if(command == 'f') /* validate float */ + str = str_skiptoblank(str); + else if(command == 's') /* validate string */ + str = str_skiptoblank(str); + + if(str[0] != 0) /* check for end of string */ + { + str[0] = 0; + str++; + } + } + } + } + + return error; +} + +const char *console_arg_string(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return ""; + return result->args[index]; +} + +int console_arg_int(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return 0; + return atoi(result->args[index]); +} + +float console_arg_float(void *res, unsigned index) +{ + PARSE_RESULT *result = (PARSE_RESULT *)res; + if (index < 0 || index >= result->num_args) + return 0.0f; + return atof(result->args[index]); +} + +int console_arg_num(void *result) +{ + return ((PARSE_RESULT *)result)->num_args; +} + +static COMMAND *first_command = 0x0; + +COMMAND *console_find_command(const char *name) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if (strcmp(cmd->name, name) == 0) + return cmd; + } + + return 0x0; +} + +void console_register(COMMAND *cmd) +{ + cmd->next = first_command; + first_command = cmd; +} + +static void (*print_callback)(const char *, void *) = 0x0; +static void *print_callback_userdata; + +void console_register_print_callback(void (*callback)(const char *, void *), void *user_data) +{ + print_callback = callback; + print_callback_userdata = user_data; +} + +void console_print(const char *str) +{ + if (print_callback) + print_callback(str, print_callback_userdata); +} + +void console_execute_line_stroked(int stroke, const char *str) +{ + PARSE_RESULT result; + COMMAND *command; + + char strokestr[2] = {'0', 0}; + if(stroke) + strokestr[0] = '1'; + + while(str) + { + const char *end = str; + const char *next_part = 0; + int in_string = 0; + + while(*end) + { + if(*end == '"') + in_string ^= 1; + else if(*end == '\\') /* escape sequences */ + { + if(end[1] == '"') + end++; + } + else if(!in_string) + { + if(*end == ';') /* command separator */ + { + next_part = end+1; + break; + } + else if(*end == '#') /* comment, no need to do anything more */ + break; + } + + end++; + } + + if(console_parse_start(&result, str, (end-str) + 1) != 0) + return; + + command = console_find_command(result.command); + + if(command) + { + int is_stroke_command = 0; + if(result.command[0] == '+') + { + /* insert the stroke direction token */ + result.args[result.num_args] = strokestr; + result.num_args++; + is_stroke_command = 1; + } + + if(stroke || is_stroke_command) + { + if(console_parse_args(&result, command->params)) + { + char buf[256]; + str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params); + console_print(buf); + } + else + command->callback(&result, command->user_data); + } + } + else + { + char buf[256]; + str_format(buf, sizeof(buf), "No such command: %s.", result.command); + console_print(buf); + } + + str = next_part; + } +} + +void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if(cmd->flags&flagmask) + { + if(str_find_nocase(cmd->name, str)) + callback(cmd->name, user); + } + } +} + + +COMMAND *console_get_command(const char *str) +{ + COMMAND *cmd; + for (cmd = first_command; cmd; cmd = cmd->next) + { + if(str_comp_nocase(cmd->name, str) == 0) + return cmd; + } + + return 0x0; +} + +void console_execute_line(const char *str) +{ + console_execute_line_stroked(1, str); +} + +static void console_execute_file_real(const char *filename) +{ + IOHANDLE file; + file = engine_openfile(filename, IOFLAG_READ); + + if(file) + { + char *line; + LINEREADER lr; + + dbg_msg("console", "executing '%s'", filename); + linereader_init(&lr, file); + + while((line = linereader_get(&lr))) + console_execute_line(line); + + io_close(file); + } + else + dbg_msg("console", "failed to open '%s'", filename); +} + +struct EXECFILE +{ + const char *filename; + struct EXECFILE *next; +}; + +void console_execute_file(const char *filename) +{ + static struct EXECFILE *first = 0; + struct EXECFILE this_file; + struct EXECFILE *cur; + struct EXECFILE *prev; + + /* make sure that this isn't being executed already */ + for(cur = first; cur; cur = cur->next) + if(strcmp(filename, cur->filename) == 0) + return; + + /* push this one to the stack */ + prev = first; + this_file.filename = filename; + this_file.next = first; + first = &this_file; + + /* execute file */ + console_execute_file_real(filename); + + /* pop this one from the stack */ + first = prev; +} + +static void con_echo(void *result, void *user_data) +{ + console_print(console_arg_string(result, 0)); +} + +static void con_exec(void *result, void *user_data) +{ + console_execute_file(console_arg_string(result, 0)); + +} + + +typedef struct +{ + CONFIG_INT_GETTER getter; + CONFIG_INT_SETTER setter; +} INT_VARIABLE_DATA; + +typedef struct +{ + CONFIG_STR_GETTER getter; + CONFIG_STR_SETTER setter; +} STR_VARIABLE_DATA; + +static void int_variable_command(void *result, void *user_data) +{ + INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data; + + if(console_arg_num(result)) + data->setter(&config, console_arg_int(result, 0)); + else + { + char buf[1024]; + str_format(buf, sizeof(buf), "Value: %d", data->getter(&config)); + console_print(buf); + } +} + +static void str_variable_command(void *result, void *user_data) +{ + STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data; + + if(console_arg_num(result)) + data->setter(&config, console_arg_string(result, 0)); + else + { + char buf[1024]; + str_format(buf, sizeof(buf), "Value: %s", data->getter(&config)); + console_print(buf); + } +} + +static void console_chain(void *result, void *user_data) +{ + COMMANDCHAIN *info = (COMMANDCHAIN *)user_data; + info->chain_callback(result, info->user_data, info->callback, info->callback_user_data); +} + +void console_chain_command(const char *cmd, COMMANDCHAIN *chaininfo, CONSOLE_CHAIN_CALLBACK cb, void *user) +{ + COMMAND *command = console_get_command(cmd); + + /* store info */ + chaininfo->chain_callback = cb; + chaininfo->callback = command->callback; + chaininfo->callback_user_data = command->user_data; + chaininfo->user_data = user; + + /* chain */ + command->callback = console_chain; + command->user_data = chaininfo; +} + +void console_init() +{ + MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text"); + MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file"); + + #define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) } + #define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} |