tron@2186: /* $Id$ */ tron@2186: darkvater@135: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@507: #include "table/strings.h" tron@2163: #include "functions.h" darkvater@135: #include "window.h" darkvater@135: #include "gui.h" darkvater@135: #include "gfx.h" darkvater@135: #include "player.h" darkvater@135: #include "variables.h" Darkvater@1390: #include "string.h" darkvater@135: #include darkvater@289: #include signde@274: #include "console.h" truelight@543: #include "network.h" truelight@1026: #include "network_data.h" truelight@1026: #include "network_server.h" signde@274: darkvater@301: #define ICON_BUFFER 79 Darkvater@1739: #define ICON_HISTORY_SIZE 20 darkvater@301: #define ICON_LINE_HEIGHT 12 truelight@543: #define ICON_RIGHT_BORDERWIDTH 10 truelight@543: #define ICON_BOTTOM_BORDERWIDTH 12 truelight@634: #define ICON_MAX_ALIAS_LINES 40 Darkvater@1739: #define ICON_TOKEN_COUNT 20 darkvater@289: darkvater@135: // ** main console ** // Darkvater@1397: static Window *_iconsole_win; // Pointer to console window darkvater@135: static bool _iconsole_inited; Darkvater@1739: static char *_iconsole_buffer[ICON_BUFFER + 1]; truelight@543: static uint16 _iconsole_cbuffer[ICON_BUFFER + 1]; Darkvater@1390: static Textbuf _iconsole_cmdline; darkvater@135: static byte _iconsole_scroll; darkvater@135: signde@220: // ** stdlib ** // darkvater@289: byte _stdlib_developer = 1; darkvater@289: bool _stdlib_con_developer = false; Darkvater@1739: FILE *_iconsole_output_file; signde@220: darkvater@301: // ** main console cmd buffer Darkvater@1739: static char *_iconsole_history[ICON_HISTORY_SIZE]; Darkvater@1739: static byte _iconsole_historypos; signde@220: darkvater@135: /* *************** */ darkvater@135: /* end of header */ signde@274: /* *************** */ signde@274: darkvater@289: static void IConsoleClearCommand(void) darkvater@135: { Darkvater@1390: memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE); Darkvater@1390: _iconsole_cmdline.length = 0; Darkvater@1390: _iconsole_cmdline.width = 0; Darkvater@1390: _iconsole_cmdline.caretpos = 0; Darkvater@1390: _iconsole_cmdline.caretxoffs = 0; darkvater@289: SetWindowDirty(_iconsole_win); darkvater@135: } darkvater@135: Darkvater@1739: static inline void IConsoleResetHistoryPos(void) {_iconsole_historypos = ICON_HISTORY_SIZE - 1;} Darkvater@1739: tron@2817: tron@2817: static void IConsoleHistoryAdd(const char* cmd); tron@2817: static void IConsoleHistoryNavigate(int direction); tron@2817: Darkvater@1390: // ** console window ** // darkvater@289: static void IConsoleWndProc(Window* w, WindowEvent* e) darkvater@135: { Darkvater@1739: switch (e->event) { Darkvater@1390: case WE_PAINT: { darkvater@289: int i = _iconsole_scroll; darkvater@301: int max = (w->height / ICON_LINE_HEIGHT) - 1; truelight@543: int delta = 0; darkvater@289: GfxFillRect(w->left, w->top, w->width, w->height - 1, 0); darkvater@986: while ((i > 0) && (i > _iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) { darkvater@289: DoDrawString(_iconsole_buffer[i], 5, darkvater@301: w->height - (_iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]); darkvater@289: i--; darkvater@135: } Darkvater@1390: /* If the text is longer than the window, don't show the starting ']' */ Darkvater@1390: delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH; truelight@543: if (delta > 0) { Darkvater@1805: DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd); truelight@543: delta = 0; truelight@543: } truelight@543: Darkvater@1805: DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd); Darkvater@1390: Darkvater@1390: if (_iconsole_cmdline.caret) Darkvater@1390: DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12); darkvater@289: break; darkvater@135: } Darkvater@1390: case WE_MOUSELOOP: Darkvater@1390: if (HandleCaret(&_iconsole_cmdline)) Darkvater@1390: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WE_DESTROY: darkvater@289: _iconsole_win = NULL; darkvater@289: _iconsole_mode = ICONSOLE_CLOSED; darkvater@289: break; darkvater@289: case WE_KEYPRESS: darkvater@289: e->keypress.cont = false; darkvater@289: switch (e->keypress.keycode) { darkvater@289: case WKC_UP: Darkvater@1739: IConsoleHistoryNavigate(+1); darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_DOWN: Darkvater@1739: IConsoleHistoryNavigate(-1); darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_SHIFT | WKC_PAGEUP: darkvater@301: if (_iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) darkvater@289: _iconsole_scroll = 0; darkvater@289: else darkvater@301: _iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1; darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_SHIFT | WKC_PAGEDOWN: darkvater@301: if (_iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) darkvater@301: _iconsole_scroll = ICON_BUFFER; darkvater@289: else darkvater@301: _iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1; darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_SHIFT | WKC_UP: darkvater@289: if (_iconsole_scroll <= 0) darkvater@289: _iconsole_scroll = 0; darkvater@289: else darkvater@289: --_iconsole_scroll; darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_SHIFT | WKC_DOWN: darkvater@301: if (_iconsole_scroll >= ICON_BUFFER) darkvater@301: _iconsole_scroll = ICON_BUFFER; darkvater@289: else darkvater@289: ++_iconsole_scroll; darkvater@289: SetWindowDirty(w); darkvater@289: break; darkvater@289: case WKC_BACKQUOTE: darkvater@289: IConsoleSwitch(); darkvater@289: break; Darkvater@1390: case WKC_RETURN: case WKC_NUM_ENTER: Darkvater@1805: IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf); Darkvater@1739: IConsoleHistoryAdd(_iconsole_cmdline.buf); darkvater@672: Darkvater@1390: IConsoleCmdExec(_iconsole_cmdline.buf); darkvater@289: IConsoleClearCommand(); darkvater@289: break; truelight@543: case WKC_CTRL | WKC_RETURN: Darkvater@1390: _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL; truelight@543: IConsoleResize(); truelight@543: MarkWholeScreenDirty(); truelight@543: break; Darkvater@1390: case (WKC_CTRL | 'V'): Darkvater@1739: if (InsertTextBufferClipboard(&_iconsole_cmdline)) { Darkvater@1739: IConsoleResetHistoryPos(); Darkvater@1390: SetWindowDirty(w); Darkvater@1739: } Darkvater@1390: break; Darkvater@1879: case (WKC_CTRL | 'L'): Darkvater@1827: IConsoleCmdExec("clear"); Darkvater@1827: break; Darkvater@1879: case (WKC_CTRL | 'U'): Darkvater@1879: DeleteTextBufferAll(&_iconsole_cmdline); Darkvater@1879: SetWindowDirty(w); Darkvater@1879: break; Darkvater@1390: case WKC_BACKSPACE: case WKC_DELETE: Darkvater@1739: if (DeleteTextBufferChar(&_iconsole_cmdline, e->keypress.keycode)) { Darkvater@1739: IConsoleResetHistoryPos(); Darkvater@1390: SetWindowDirty(w); Darkvater@1739: } Darkvater@1390: break; Darkvater@1390: case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME: Darkvater@1739: if (MoveTextBufferPos(&_iconsole_cmdline, e->keypress.keycode)) { Darkvater@1739: IConsoleResetHistoryPos(); Darkvater@1390: SetWindowDirty(w); Darkvater@1739: } darkvater@289: break; darkvater@289: default: Darkvater@1390: if (IsValidAsciiChar(e->keypress.ascii)) { darkvater@301: _iconsole_scroll = ICON_BUFFER; Darkvater@1390: InsertTextBufferChar(&_iconsole_cmdline, e->keypress.ascii); Darkvater@1739: IConsoleResetHistoryPos(); darkvater@289: SetWindowDirty(w); Darkvater@1390: } else darkvater@289: e->keypress.cont = true; darkvater@289: break; darkvater@289: } darkvater@135: } darkvater@135: } darkvater@135: Darkvater@1390: static const Widget _iconsole_window_widgets[] = { Darkvater@1390: {WIDGETS_END} Darkvater@1390: }; Darkvater@1390: Darkvater@1390: static const WindowDesc _iconsole_window_desc = { Darkvater@1390: 0, 0, 2, 2, Darkvater@1390: WC_CONSOLE, 0, Darkvater@1390: WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS, Darkvater@1390: _iconsole_window_widgets, Darkvater@1390: IConsoleWndProc, Darkvater@1390: }; Darkvater@1390: darkvater@289: void IConsoleInit(void) darkvater@135: { Darkvater@1739: extern const char _openttd_revision[]; darkvater@247: _iconsole_output_file = NULL; Darkvater@1805: _icolour_def = 1; Darkvater@1805: _icolour_err = 3; Darkvater@1805: _icolour_warn = 13; Darkvater@1805: _icolour_dbg = 5; Darkvater@1805: _icolour_cmd = 2; darkvater@301: _iconsole_scroll = ICON_BUFFER; Darkvater@1739: _iconsole_historypos = ICON_HISTORY_SIZE - 1; darkvater@289: _iconsole_inited = true; darkvater@289: _iconsole_mode = ICONSOLE_CLOSED; darkvater@289: _iconsole_win = NULL; darkvater@1243: darkvater@1243: #ifdef ENABLE_NETWORK /* Initialize network only variables */ darkvater@1243: _redirect_console_to_client = 0; darkvater@1243: #endif darkvater@1243: Darkvater@1739: memset(_iconsole_history, 0, sizeof(_iconsole_history)); darkvater@1243: memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer)); darkvater@1243: memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer)); Darkvater@1390: _iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it Darkvater@1390: _iconsole_cmdline.maxlength = ICON_CMDLN_SIZE - 1; darkvater@1243: Darkvater@1739: IConsolePrintF(13, "OpenTTD Game Console Revision 7 - %s", _openttd_revision); Darkvater@1739: IConsolePrint(12, "------------------------------------"); Darkvater@1739: IConsolePrint(12, "use \"help\" for more information"); Darkvater@1739: IConsolePrint(12, ""); signde@220: IConsoleStdLibRegister(); signde@220: IConsoleClearCommand(); Darkvater@1739: IConsoleHistoryAdd(""); darkvater@135: } darkvater@135: Darkvater@1827: void IConsoleClearBuffer(void) darkvater@135: { darkvater@289: uint i; Darkvater@1827: for (i = 0; i <= ICON_BUFFER; i++) { signde@220: free(_iconsole_buffer[i]); Darkvater@1827: _iconsole_buffer[i] = NULL; Darkvater@1827: } Darkvater@1827: } Darkvater@1390: Darkvater@1827: static void IConsoleClear(void) Darkvater@1827: { Darkvater@1390: free(_iconsole_cmdline.buf); Darkvater@1827: IConsoleClearBuffer(); darkvater@135: } darkvater@135: darkvater@1046: static void IConsoleWriteToLogFile(const char* string) darkvater@1046: { darkvater@1046: if (_iconsole_output_file != NULL) { darkvater@1046: // if there is an console output file ... also print it there darkvater@1046: fwrite(string, strlen(string), 1, _iconsole_output_file); darkvater@1046: fwrite("\n", 1, 1, _iconsole_output_file); darkvater@1046: } darkvater@1046: } darkvater@1046: darkvater@1046: bool CloseConsoleLogIfActive(void) darkvater@1046: { darkvater@1046: if (_iconsole_output_file != NULL) { Darkvater@1805: IConsolePrintF(_icolour_def, "file output complete"); darkvater@1046: fclose(_iconsole_output_file); darkvater@1127: _iconsole_output_file = NULL; darkvater@1046: return true; darkvater@1046: } darkvater@1046: darkvater@1046: return false; darkvater@1046: } darkvater@1046: darkvater@289: void IConsoleFree(void) darkvater@135: { darkvater@289: _iconsole_inited = false; signde@220: IConsoleClear(); darkvater@1046: CloseConsoleLogIfActive(); darkvater@135: } darkvater@135: darkvater@289: void IConsoleResize(void) signde@220: { darkvater@986: _iconsole_win = FindWindowById(WC_CONSOLE, 0); darkvater@986: truelight@543: switch (_iconsole_mode) { truelight@543: case ICONSOLE_OPENED: truelight@543: _iconsole_win->height = _screen.height / 3; truelight@543: _iconsole_win->width = _screen.width; truelight@543: break; truelight@543: case ICONSOLE_FULL: truelight@543: _iconsole_win->height = _screen.height - ICON_BOTTOM_BORDERWIDTH; truelight@543: _iconsole_win->width = _screen.width; truelight@543: break; Darkvater@1755: default: break; darkvater@135: } darkvater@986: darkvater@986: MarkWholeScreenDirty(); darkvater@135: } darkvater@135: darkvater@289: void IConsoleSwitch(void) darkvater@135: { darkvater@289: switch (_iconsole_mode) { darkvater@289: case ICONSOLE_CLOSED: darkvater@289: _iconsole_win = AllocateWindowDesc(&_iconsole_window_desc); darkvater@289: _iconsole_win->height = _screen.height / 3; darkvater@289: _iconsole_win->width = _screen.width; darkvater@289: _iconsole_mode = ICONSOLE_OPENED; Darkvater@1739: SETBIT(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll darkvater@289: break; Darkvater@1397: case ICONSOLE_OPENED: case ICONSOLE_FULL: darkvater@289: DeleteWindowById(WC_CONSOLE, 0); darkvater@289: _iconsole_win = NULL; darkvater@289: _iconsole_mode = ICONSOLE_CLOSED; Darkvater@1397: CLRBIT(_no_scroll, SCROLL_CON); truelight@543: break; darkvater@289: } darkvater@986: darkvater@986: MarkWholeScreenDirty(); darkvater@135: } darkvater@135: Darkvater@1739: void IConsoleClose(void) {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();} Darkvater@1739: void IConsoleOpen(void) {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();} darkvater@135: Darkvater@1739: /** Darkvater@1739: * Add the entered line into the history so you can look it back Darkvater@1739: * scroll, etc. Put it to the beginning as it is the latest text Darkvater@1739: * @param cmd Text to be entered into the 'history' Darkvater@1739: */ tron@2817: static void IConsoleHistoryAdd(const char* cmd) signde@220: { Darkvater@1739: free(_iconsole_history[ICON_HISTORY_SIZE - 1]); darkvater@141: Darkvater@1739: memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1)); Darkvater@1739: _iconsole_history[0] = strdup(cmd); Darkvater@1739: IConsoleResetHistoryPos(); signde@220: } signde@220: Darkvater@1739: /** Darkvater@1739: * Navigate Up/Down in the history of typed commands Darkvater@1739: * @param direction Go further back in history (+1), go to recently typed commands (-1) Darkvater@1739: */ tron@2817: static void IConsoleHistoryNavigate(int direction) signde@220: { Darkvater@1739: int i = _iconsole_historypos + direction; Darkvater@1739: Darkvater@1739: // watch out for overflows, just wrap around Darkvater@1739: if (i < 0) i = ICON_HISTORY_SIZE - 1; Darkvater@1739: if (i >= ICON_HISTORY_SIZE) i = 0; Darkvater@1739: darkvater@289: if (direction > 0) Darkvater@1739: if (_iconsole_history[i] == NULL) i = 0; Darkvater@1739: Darkvater@1739: if (direction < 0) { Darkvater@1739: while (i > 0 && _iconsole_history[i] == NULL) i--; Darkvater@1739: } Darkvater@1739: Darkvater@1739: _iconsole_historypos = i; signde@220: IConsoleClearCommand(); Darkvater@1739: // copy history to 'command prompt / bash' Darkvater@1739: assert(_iconsole_history[i] != NULL && IS_INT_INSIDE(i, 0, ICON_HISTORY_SIZE)); Darkvater@1739: ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength); Darkvater@1390: UpdateTextBufferSize(&_iconsole_cmdline); darkvater@141: } darkvater@141: Darkvater@1588: /** Darkvater@1588: * Handle the printing of text entered into the console or redirected there Darkvater@1588: * by any other means. Text can be redirected to other players in a network game Darkvater@1588: * as well as to a logfile. If the network server is a dedicated server, all activities Darkvater@1588: * are also logged. All lines to print are added to a temporary buffer which can be Darkvater@1588: * used as a history to print them onscreen Darkvater@1588: * @param color_code the colour of the command. Red in case of errors, etc. Darkvater@1588: * @param string the message entered or output on the console (notice, error, etc.) Darkvater@1588: */ truelight@543: void IConsolePrint(uint16 color_code, const char* string) darkvater@135: { darkvater@1243: #ifdef ENABLE_NETWORK truelight@1026: if (_redirect_console_to_client != 0) { truelight@1026: /* Redirect the string to the client */ truelight@1026: SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string); truelight@1026: return; truelight@1026: } darkvater@1243: #endif truelight@1026: truelight@543: if (_network_dedicated) { truelight@543: printf("%s\n", string); darkvater@1046: IConsoleWriteToLogFile(string); truelight@543: return; truelight@543: } truelight@543: signde@220: if (!_iconsole_inited) return; darkvater@135: Darkvater@1588: /* move up all the strings in the buffer one place and do the same for colour Darkvater@1588: * to accomodate for the new command/message */ Darkvater@1588: free(_iconsole_buffer[0]); Darkvater@1588: memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER); Darkvater@1588: _iconsole_buffer[ICON_BUFFER] = strdup(string); signde@220: Darkvater@1739: { // filter out unprintable characters Darkvater@1739: char *i; Darkvater@1739: for (i = _iconsole_buffer[ICON_BUFFER]; *i != '\0'; i++) Darkvater@1739: if (!IsValidAsciiChar((byte)*i)) *i = ' '; Darkvater@1739: } Darkvater@1588: Darkvater@1588: memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER); Darkvater@1588: _iconsole_cbuffer[ICON_BUFFER] = color_code; darkvater@289: darkvater@1046: IConsoleWriteToLogFile(string); dominik@650: darkvater@289: if (_iconsole_win != NULL) SetWindowDirty(_iconsole_win); darkvater@135: } darkvater@135: Darkvater@1739: /** Darkvater@1739: * Handle the printing of text entered into the console or redirected there Darkvater@1739: * by any other means. Uses printf() style format, for more information look Darkvater@1739: * at @IConsolePrint() Darkvater@1739: */ Darkvater@1739: void CDECL IConsolePrintF(uint16 color_code, const char *s, ...) darkvater@135: { darkvater@135: va_list va; Darkvater@1739: char buf[ICON_MAX_STREAMSIZE]; darkvater@247: darkvater@135: va_start(va, s); Darkvater@1739: vsnprintf(buf, sizeof(buf), s, va); darkvater@135: va_end(va); darkvater@247: darkvater@289: IConsolePrint(color_code, buf); darkvater@135: } darkvater@135: Darkvater@1739: /** Darkvater@1739: * It is possible to print debugging information to the console, Darkvater@1739: * which is achieved by using this function. Can only be used by Darkvater@1739: * @debug() in debug.c. You need at least a level 2 (developer) for debugging Darkvater@1739: * messages to show up Darkvater@1739: */ darkvater@289: void IConsoleDebug(const char* string) signde@220: { darkvater@289: if (_stdlib_developer > 1) Darkvater@1805: IConsolePrintF(_icolour_dbg, "dbg: %s", string); darkvater@247: } darkvater@135: Darkvater@1739: /** Darkvater@1739: * It is possible to print warnings to the console. These are mostly Darkvater@1739: * errors or mishaps, but non-fatal. You need at least a level 1 (developer) for Darkvater@1739: * debugging messages to show up Darkvater@1739: */ darkvater@289: void IConsoleWarning(const char* string) darkvater@289: { darkvater@289: if (_stdlib_developer > 0) Darkvater@1805: IConsolePrintF(_icolour_warn, "WARNING: %s", string); darkvater@289: } darkvater@289: Darkvater@1739: /** Darkvater@1739: * It is possible to print error information to the console. This can include Darkvater@1739: * game errors, or errors in general you would want the user to notice Darkvater@1739: */ Darkvater@1739: void IConsoleError(const char* string) darkvater@289: { Darkvater@1805: IConsolePrintF(_icolour_err, "ERROR: %s", string); signde@220: } signde@220: Darkvater@1739: /** Darkvater@1739: * Change a string into its number representation. Supports Darkvater@1739: * decimal and hexadecimal numbers as well as 'on'/'off' 'true'/'false' Darkvater@1739: * @param *value the variable a successfull conversion will be put in Darkvater@1739: * @param *arg the string to be converted Darkvater@1739: * @return Return true on success or false on failure Darkvater@1739: */ Darkvater@1739: bool GetArgumentInteger(uint32 *value, const char *arg) signde@220: { Darkvater@1773: char *endptr; signde@220: Darkvater@1773: if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) { Darkvater@1773: *value = 1; Darkvater@1773: return true; Darkvater@1773: } Darkvater@1773: if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) { Darkvater@1773: *value = 0; Darkvater@1773: return true; Darkvater@1773: } Darkvater@1739: Darkvater@1773: *value = strtoul(arg, &endptr, 0); tron@1899: return arg != endptr; signde@220: } signde@220: Darkvater@1833: // * ************************* * // Darkvater@1833: // * hooking code * // Darkvater@1833: // * ************************* * // Darkvater@1833: /** Darkvater@1833: * General internal hooking code that is the same for both commands and variables Darkvater@1833: * @param hooks @IConsoleHooks structure that will be set according to Darkvater@1833: * @param type type access trigger Darkvater@1833: * @param proc function called when the hook criteria is met Darkvater@1833: */ Darkvater@1833: static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc) Darkvater@1833: { Darkvater@1833: if (hooks == NULL || proc == NULL) return; Darkvater@1833: Darkvater@1833: switch (type) { Darkvater@1833: case ICONSOLE_HOOK_ACCESS: Darkvater@1833: hooks->access = proc; Darkvater@1833: break; Darkvater@1833: case ICONSOLE_HOOK_PRE_ACTION: Darkvater@1833: hooks->pre = proc; Darkvater@1833: break; Darkvater@1833: case ICONSOLE_HOOK_POST_ACTION: Darkvater@1833: hooks->post = proc; Darkvater@1833: break; Darkvater@1889: default: NOT_REACHED(); Darkvater@1833: } Darkvater@1833: } Darkvater@1833: Darkvater@1833: /** Darkvater@1833: * Handle any special hook triggers. If the hook type is met check if Darkvater@1833: * there is a function associated with that and if so, execute it Darkvater@1833: * @param hooks @IConsoleHooks structure that will be checked Darkvater@1833: * @param type type of hook, trigger that needs to be activated Darkvater@1833: * @return true on a successfull execution of the hook command or if there Darkvater@1833: * is no hook/trigger present at all. False otherwise Darkvater@1833: */ Darkvater@1833: static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type) Darkvater@1833: { Darkvater@1833: IConsoleHook *proc = NULL; Darkvater@1833: if (hooks == NULL) return false; Darkvater@1833: Darkvater@1833: switch (type) { Darkvater@1833: case ICONSOLE_HOOK_ACCESS: Darkvater@1833: proc = hooks->access; Darkvater@1833: break; Darkvater@1833: case ICONSOLE_HOOK_PRE_ACTION: Darkvater@1833: proc = hooks->pre; Darkvater@1833: break; Darkvater@1833: case ICONSOLE_HOOK_POST_ACTION: Darkvater@1833: proc = hooks->post; Darkvater@1833: break; Darkvater@1889: default: NOT_REACHED(); Darkvater@1833: } Darkvater@1833: Darkvater@1833: return (proc == NULL) ? true : proc(); Darkvater@1833: } Darkvater@1833: Darkvater@1833: /** Darkvater@1833: * Add a hook to a command that will be triggered at certain points Darkvater@1833: * @param name name of the command that the hook is added to Darkvater@1833: * @param type type of hook that is added (ACCESS, BEFORE and AFTER change) Darkvater@1833: * @param proc function called when the hook criteria is met Darkvater@1833: */ Darkvater@1833: void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc) Darkvater@1833: { Darkvater@1833: IConsoleCmd *cmd = IConsoleCmdGet(name); Darkvater@1833: if (cmd == NULL) return; Darkvater@1833: IConsoleHookAdd(&cmd->hook, type, proc); Darkvater@1833: } Darkvater@1833: Darkvater@1833: /** Darkvater@1833: * Add a hook to a variable that will be triggered at certain points Darkvater@1833: * @param name name of the variable that the hook is added to Darkvater@1833: * @param type type of hook that is added (ACCESS, BEFORE and AFTER change) Darkvater@1833: * @param proc function called when the hook criteria is met Darkvater@1833: */ Darkvater@1833: void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc) Darkvater@1833: { Darkvater@1833: IConsoleVar *var = IConsoleVarGet(name); Darkvater@1833: if (var == NULL) return; Darkvater@1833: IConsoleHookAdd(&var->hook, type, proc); Darkvater@1833: } Darkvater@1833: Darkvater@1739: /** Darkvater@1739: * Perhaps ugly macro, but this saves us the trouble of writing the same function Darkvater@1739: * three types, just with different variables. Yes, templates would be handy. It was Darkvater@1739: * either this define or an even more ugly void* magic function Darkvater@1739: */ Darkvater@1739: #define IConsoleAddSorted(_base, item_new, IConsoleType, type) \ Darkvater@1739: { \ Darkvater@1739: IConsoleType *item, *item_before; \ Darkvater@1739: /* first command */ \ Darkvater@1739: if (_base == NULL) { \ Darkvater@1739: _base = item_new; \ Darkvater@1739: return; \ Darkvater@1739: } \ Darkvater@1739: \ Darkvater@1739: item_before = NULL; \ Darkvater@1739: item = _base; \ Darkvater@1739: \ Darkvater@1739: /* BEGIN - Alphabetically insert the commands into the linked list */ \ Darkvater@1739: while (item != NULL) { \ Darkvater@1739: int i = strcmp(item->name, item_new->name); \ Darkvater@1739: if (i == 0) { \ Darkvater@1739: IConsoleError(type " with this name already exists; insertion aborted"); \ Darkvater@1739: free(item_new); \ Darkvater@1739: return; \ Darkvater@1739: } \ Darkvater@1739: \ Darkvater@1739: if (i > 0) break; /* insert at this position */ \ Darkvater@1739: \ Darkvater@1739: item_before = item; \ Darkvater@1739: item = item->next; \ Darkvater@1739: } \ Darkvater@1739: \ Darkvater@1739: if (item_before == NULL) { \ Darkvater@1739: _base = item_new; \ Darkvater@1739: } else \ Darkvater@1739: item_before->next = item_new; \ Darkvater@1739: \ Darkvater@1739: item_new->next = item; \ Darkvater@1739: /* END - Alphabetical insert */ \ truelight@634: } truelight@634: Darkvater@1739: /** Darkvater@1739: * Register a new command to be used in the console Darkvater@1739: * @param name name of the command that will be used Darkvater@1739: * @param proc function that will be called upon execution of command Darkvater@1739: */ Darkvater@1739: void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc) truelight@634: { Darkvater@1739: char *new_cmd = strdup(name); Darkvater@1739: IConsoleCmd *item_new = malloc(sizeof(IConsoleCmd)); truelight@634: Darkvater@1739: item_new->next = NULL; Darkvater@1739: item_new->proc = proc; Darkvater@1739: item_new->name = new_cmd; Darkvater@1739: Darkvater@1739: item_new->hook.access = NULL; Darkvater@1739: item_new->hook.pre = NULL; Darkvater@1739: item_new->hook.post = NULL; Darkvater@1739: Darkvater@1739: IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command"); Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Find the command pointed to by its string Darkvater@1739: * @param name command to be found Darkvater@1739: * @return return Cmdstruct of the found command, or NULL on failure Darkvater@1739: */ Darkvater@1739: IConsoleCmd *IConsoleCmdGet(const char *name) Darkvater@1739: { Darkvater@1739: IConsoleCmd *item; Darkvater@1739: Darkvater@1739: for (item = _iconsole_cmds; item != NULL; item = item->next) { truelight@634: if (strcmp(item->name, name) == 0) return item; truelight@634: } truelight@634: return NULL; truelight@634: } truelight@634: Darkvater@1739: /** Darkvater@1739: * Register a an alias for an already existing command in the console Darkvater@1739: * @param name name of the alias that will be used Darkvater@1739: * @param cmd name of the command that 'name' will be alias of Darkvater@1739: */ Darkvater@1739: void IConsoleAliasRegister(const char *name, const char *cmd) Darkvater@1725: { Darkvater@1739: char *new_alias = strdup(name); Darkvater@1739: char *cmd_aliased = strdup(cmd); Darkvater@1739: IConsoleAlias *item_new = malloc(sizeof(IConsoleAlias)); truelight@634: Darkvater@1739: item_new->next = NULL; Darkvater@1739: item_new->cmdline = cmd_aliased; Darkvater@1739: item_new->name = new_alias; Darkvater@1739: Darkvater@1739: IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias"); Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Find the alias pointed to by its string Darkvater@1739: * @param name alias to be found Darkvater@1739: * @return return Aliasstruct of the found alias, or NULL on failure Darkvater@1739: */ Darkvater@1739: IConsoleAlias *IConsoleAliasGet(const char *name) Darkvater@1739: { Darkvater@1739: IConsoleAlias* item; Darkvater@1739: Darkvater@1739: for (item = _iconsole_aliases; item != NULL; item = item->next) { Darkvater@1739: if (strcmp(item->name, name) == 0) return item; truelight@634: } truelight@634: signde@220: return NULL; darkvater@135: } darkvater@135: Darkvater@1739: /** copy in an argument into the aliasstream */ Darkvater@1739: static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos) signde@220: { Darkvater@1739: int len = min(ICON_MAX_STREAMSIZE - bufpos, strlen(src)); Darkvater@1739: strncpy(dst, src, len); Darkvater@1739: Darkvater@1739: return len; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * An alias is just another name for a command, or for more commands Darkvater@1739: * Execute it as well. Darkvater@1739: * @param *alias is the alias of the command Darkvater@1739: * @param tokencount the number of parameters passed Darkvater@1739: * @param *tokens are the parameters given to the original command (0 is the first param) Darkvater@1739: */ tron@2817: static void IConsoleAliasExec(const IConsoleAlias* alias, byte tokencount, char* tokens[ICON_TOKEN_COUNT]) Darkvater@1739: { Darkvater@1739: const char *cmdptr; Darkvater@1739: char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE]; Darkvater@1739: int i; Darkvater@1739: uint a_index, astream_i; Darkvater@1739: Darkvater@1739: memset(&aliases, 0, sizeof(aliases)); Darkvater@1739: memset(&aliasstream, 0, sizeof(aliasstream)); Darkvater@1739: Darkvater@1866: if (_stdlib_con_developer) Darkvater@1866: IConsolePrintF(_icolour_dbg, "condbg: requested command is an alias; parsing..."); Darkvater@1866: Darkvater@1739: aliases[0] = aliasstream; tron@1819: for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) { Darkvater@1739: if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break; Darkvater@1739: Darkvater@1739: switch (*cmdptr) { Darkvater@1739: case '\'': /* ' will double for "" */ Darkvater@1739: aliasstream[astream_i++] = '"'; darkvater@289: break; Darkvater@1739: case ';': /* Cmd seperator, start new command */ Darkvater@1739: aliasstream[astream_i] = '\0'; Darkvater@1739: aliases[++a_index] = &aliasstream[++astream_i]; tron@1819: cmdptr++; darkvater@289: break; Darkvater@1739: case '%': /* Some or all parameters */ tron@1819: cmdptr++; Darkvater@1739: switch (*cmdptr) { Darkvater@1739: case '+': { /* All parameters seperated: "[param 1]" "[param 2]" */ Darkvater@1739: for (i = 0; i != tokencount; i++) { Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i); Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: aliasstream[astream_i++] = ' '; Darkvater@1739: } Darkvater@1739: } break; Darkvater@1739: case '!': { /* Merge the parameters to one: "[param 1] [param 2] [param 3...]" */ Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: for (i = 0; i != tokencount; i++) { Darkvater@1739: astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i); Darkvater@1739: aliasstream[astream_i++] = ' '; Darkvater@1739: } Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: Darkvater@1739: } break; Darkvater@1739: default: { /* One specific parameter: %A = [param 1] %B = [param 2] ... */ Darkvater@1739: int param = *cmdptr - 'A'; Darkvater@1739: Darkvater@1739: if (param < 0 || param >= tokencount) { Darkvater@1739: IConsoleError("too many or wrong amount of parameters passed to alias, aborting"); Darkvater@1805: IConsolePrintF(_icolour_warn, "Usage of alias '%s': %s", alias->name, alias->cmdline); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i); Darkvater@1739: aliasstream[astream_i++] = '"'; Darkvater@1739: } break; Darkvater@1739: } break; Darkvater@1739: Darkvater@1739: default: Darkvater@1739: aliasstream[astream_i++] = *cmdptr; darkvater@289: break; Darkvater@1739: } darkvater@289: } signde@220: Darkvater@1739: for (i = 0; i <= (int)a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn darkvater@222: } signde@220: Darkvater@1739: /** Darkvater@1739: * Special function for adding string-type variables. They in addition Darkvater@1739: * also need a 'size' value saying how long their string buffer is. Darkvater@1739: * @param size the length of the string buffer Darkvater@1739: * For more information see @IConsoleVarRegister() Darkvater@1739: */ Darkvater@1739: void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help) Darkvater@1739: { Darkvater@1739: IConsoleVar *var; Darkvater@1739: IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help); Darkvater@1739: var = IConsoleVarGet(name); Darkvater@1739: var->size = size; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Register a new variable to be used in the console Darkvater@1739: * @param name name of the variable that will be used Darkvater@1739: * @param addr memory location the variable will point to Darkvater@1739: * @param help the help string shown for the variable Darkvater@1739: * @param type the type of the variable (simple atomic) so we know which values it can get Darkvater@1739: */ Darkvater@1739: void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help) Darkvater@1739: { Darkvater@1739: char *new_cmd = strdup(name); Darkvater@1739: IConsoleVar *item_new = malloc(sizeof(IConsoleVar)); Darkvater@1739: Darkvater@1739: item_new->help = (help != NULL) ? strdup(help) : NULL; Darkvater@1739: Darkvater@1739: item_new->next = NULL; Darkvater@1739: item_new->name = new_cmd; Darkvater@1739: item_new->addr = addr; Darkvater@1739: item_new->proc = NULL; Darkvater@1739: item_new->type = type; Darkvater@1739: Darkvater@1739: item_new->hook.access = NULL; Darkvater@1739: item_new->hook.pre = NULL; Darkvater@1739: item_new->hook.post = NULL; Darkvater@1739: Darkvater@1739: IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable"); Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Find the variable pointed to by its string Darkvater@1739: * @param name variable to be found Darkvater@1739: * @return return Varstruct of the found variable, or NULL on failure Darkvater@1739: */ Darkvater@1739: IConsoleVar *IConsoleVarGet(const char *name) Darkvater@1739: { Darkvater@1739: IConsoleVar *item; Darkvater@1739: for (item = _iconsole_vars; item != NULL; item = item->next) { Darkvater@1739: if (strcmp(item->name, name) == 0) return item; Darkvater@1739: } Darkvater@1739: Darkvater@1739: return NULL; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Set a new value to a console variable Darkvater@1739: * @param *var the variable being set/changed Darkvater@1739: * @param value the new value given to the variable, cast properly Darkvater@1739: */ Darkvater@1739: static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value) Darkvater@1739: { Darkvater@1833: IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION); signde@220: switch (var->type) { darkvater@289: case ICONSOLE_VAR_BOOLEAN: Darkvater@1739: *(bool*)var->addr = (value != 0); darkvater@289: break; darkvater@289: case ICONSOLE_VAR_BYTE: Darkvater@1739: *(byte*)var->addr = (byte)value; darkvater@289: break; darkvater@289: case ICONSOLE_VAR_UINT16: tron@2145: *(uint16*)var->addr = (uint16)value; Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_INT16: Darkvater@1739: *(int16*)var->addr = (int16)value; darkvater@289: break; darkvater@289: case ICONSOLE_VAR_UINT32: Darkvater@1739: *(uint32*)var->addr = (uint32)value; darkvater@289: break; darkvater@289: case ICONSOLE_VAR_INT32: Darkvater@1739: *(int32*)var->addr = (int32)value; darkvater@289: break; Darkvater@1739: default: NOT_REACHED(); darkvater@222: } Darkvater@1739: Darkvater@1833: IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION); Darkvater@1739: IConsoleVarPrintSetValue(var); darkvater@135: } darkvater@135: Darkvater@1739: /** Darkvater@1739: * Set a new value to a string-type variable. Basically this Darkvater@1739: * means to copy the new value over to the container. Darkvater@1739: * @param *var the variable in question Darkvater@1739: * @param *value the new value Darkvater@1739: */ Darkvater@1739: static void IConsoleVarSetStringvalue(const IConsoleVar *var, char *value) signde@220: { Darkvater@1739: if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return; Darkvater@1739: Darkvater@1833: IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION); Darkvater@1739: ttd_strlcpy((char*)var->addr, (char*)value, var->size); Darkvater@1833: IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION); Darkvater@1739: IConsoleVarPrintSetValue(var); // print out the new value, giving feedback Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Query the current value of a variable and return it Darkvater@1739: * @param *var the variable queried Darkvater@1739: * @return current value of the variable Darkvater@1739: */ Darkvater@1739: static uint32 IConsoleVarGetValue(const IConsoleVar *var) Darkvater@1739: { Darkvater@1739: uint32 result = 0; darkvater@135: darkvater@135: switch (var->type) { darkvater@135: case ICONSOLE_VAR_BOOLEAN: Darkvater@1739: result = *(bool*)var->addr; darkvater@289: break; darkvater@135: case ICONSOLE_VAR_BYTE: Darkvater@1739: result = *(byte*)var->addr; darkvater@289: break; darkvater@135: case ICONSOLE_VAR_UINT16: Darkvater@1739: result = *(uint16*)var->addr; darkvater@289: break; darkvater@135: case ICONSOLE_VAR_INT16: Darkvater@1739: result = *(int16*)var->addr; Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_UINT32: Darkvater@1739: result = *(uint32*)var->addr; darkvater@289: break; darkvater@135: case ICONSOLE_VAR_INT32: Darkvater@1739: result = *(int32*)var->addr; Darkvater@1739: break; Darkvater@1739: default: NOT_REACHED(); Darkvater@1739: } Darkvater@1739: return result; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Get the value of the variable and put it into a printable Darkvater@1739: * string form so we can use it for printing Darkvater@1739: */ Darkvater@1739: static char *IConsoleVarGetStringValue(const IConsoleVar *var) Darkvater@1739: { Darkvater@1739: static char tempres[50]; Darkvater@1739: char *value = tempres; Darkvater@1739: Darkvater@1739: switch (var->type) { Darkvater@1739: case ICONSOLE_VAR_BOOLEAN: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off"); Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_BYTE: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr); Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_UINT16: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr); Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_UINT32: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%u", *(uint32*)var->addr); Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_INT16: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr); Darkvater@1739: break; Darkvater@1739: case ICONSOLE_VAR_INT32: Darkvater@1739: snprintf(tempres, sizeof(tempres), "%i", *(int32*)var->addr); darkvater@289: break; darkvater@135: case ICONSOLE_VAR_STRING: Darkvater@1739: value = (char*)var->addr; Darkvater@1889: break; Darkvater@1739: default: NOT_REACHED(); darkvater@289: } Darkvater@1739: Darkvater@1739: return value; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Print out the value of the variable when asked Darkvater@1739: */ Darkvater@1739: void IConsoleVarPrintGetValue(const IConsoleVar *var) Darkvater@1739: { Darkvater@1739: char *value; Darkvater@1739: /* Some variables need really specific handling, handle this in its Darkvater@1739: * callback function */ Darkvater@1739: if (var->proc != NULL) { Darkvater@1739: var->proc(0, NULL); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: value = IConsoleVarGetStringValue(var); Darkvater@1805: IConsolePrintF(_icolour_warn, "Current value for '%s' is: %s", var->name, value); Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Print out the value of the variable after it has been assigned Darkvater@1739: * a new value, thus giving us feedback on the action Darkvater@1739: */ Darkvater@1739: void IConsoleVarPrintSetValue(const IConsoleVar *var) Darkvater@1739: { Darkvater@1739: char *value = IConsoleVarGetStringValue(var); Darkvater@1805: IConsolePrintF(_icolour_warn, "'%s' changed to: %s", var->name, value); Darkvater@1739: } Darkvater@1739: Darkvater@1739: /** Darkvater@1739: * Execute a variable command. Without any parameters, print out its value Darkvater@1739: * with parameters it assigns a new value to the variable Darkvater@1739: * @param *var the variable that we will be querying/changing Darkvater@1739: * @param tokencount how many additional parameters have been given to the commandline Darkvater@1739: * @param *token the actual parameters the variable was called with Darkvater@1739: */ Darkvater@1739: void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT]) Darkvater@1739: { Darkvater@1739: const char *tokenptr = token[0]; Darkvater@1739: byte t_index = tokencount; Darkvater@1739: uint32 value; Darkvater@1739: Darkvater@1866: if (_stdlib_con_developer) Darkvater@1866: IConsolePrintF(_icolour_dbg, "condbg: requested command is a variable"); Darkvater@1866: Darkvater@1739: if (tokencount == 0) { /* Just print out value */ Darkvater@1739: IConsoleVarPrintGetValue(var); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */ Darkvater@1739: if (strcmp(tokenptr, "=") == 0) tokencount--; Darkvater@1739: Darkvater@1739: if (tokencount == 1) { Darkvater@1739: /* Some variables need really special handling, handle it in their callback procedure */ Darkvater@1739: if (var->proc != NULL) { Darkvater@1739: var->proc(tokencount, &token[t_index - tokencount]); // set the new value Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: /* Strings need special processing. No need to convert the argument to Darkvater@1739: * an integer value, just copy over the argument on a one-by-one basis */ Darkvater@1739: if (var->type == ICONSOLE_VAR_STRING) { Darkvater@1739: IConsoleVarSetStringvalue(var, token[t_index - tokencount]); Darkvater@1739: return; Darkvater@1739: } else if (GetArgumentInteger(&value, token[t_index - tokencount])) { Darkvater@1739: IConsoleVarSetValue(var, value); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: /* Increase or decrease the value by one. This of course can only happen to 'number' types */ Darkvater@1739: if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) { Darkvater@1739: IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) { Darkvater@1739: IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: } Darkvater@1739: Darkvater@1739: IConsoleError("invalid variable assignment"); darkvater@135: } darkvater@135: Darkvater@1739: /** Darkvater@1739: * Add a callback function to the variable. Some variables need Darkvater@1739: * very special processing, which can only be done with custom code Darkvater@1739: * @param name name of the variable the callback function is added to Darkvater@1739: * @param proc the function called Darkvater@1739: */ Darkvater@1739: void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc) Darkvater@1739: { Darkvater@1739: IConsoleVar *var = IConsoleVarGet(name); Darkvater@1739: if (var == NULL) return; Darkvater@1739: var->proc = proc; Darkvater@1739: } tron@1109: Darkvater@1739: /** Darkvater@1739: * Execute a given command passed to us. First chop it up into Darkvater@1739: * individual tokens (seperated by spaces), then execute it if possible Darkvater@1739: * @param cmdstr string to be parsed and executed Darkvater@1739: */ Darkvater@1739: void IConsoleCmdExec(const char *cmdstr) Darkvater@1739: { Darkvater@1739: IConsoleCmd *cmd = NULL; Darkvater@1739: IConsoleAlias *alias = NULL; Darkvater@1739: IConsoleVar *var = NULL; Darkvater@1726: Darkvater@1739: const char *cmdptr; Darkvater@1739: char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE]; Darkvater@1739: uint t_index, tstream_i; Darkvater@1739: Darkvater@1739: bool longtoken = false; Darkvater@1739: bool foundtoken = false; Darkvater@1739: Darkvater@1866: if (cmdstr[0] == '#') return; // comments Darkvater@1866: tron@1819: for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) { Darkvater@1739: if (!IsValidAsciiChar(*cmdptr)) { Darkvater@1739: IConsoleError("command contains malformed characters, aborting"); Darkvater@1805: IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr); Darkvater@1739: return; tron@1379: } tron@1379: } signde@220: dominik@640: if (_stdlib_con_developer) Darkvater@1805: IConsolePrintF(_icolour_dbg, "condbg: executing cmdline: '%s'", cmdstr); signde@220: Darkvater@1739: memset(&tokens, 0, sizeof(tokens)); Darkvater@1739: memset(&tokenstream, 0, sizeof(tokenstream)); signde@220: Darkvater@1739: /* 1. Split up commandline into tokens, seperated by spaces, commands Darkvater@1739: * enclosed in "" are taken as one token. We can only go as far as the amount Darkvater@1739: * of characters in our stream or the max amount of tokens we can handle */ tron@1819: for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) { Darkvater@1739: if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break; Darkvater@1739: Darkvater@1739: switch (*cmdptr) { Darkvater@1739: case ' ': /* Token seperator */ Darkvater@1739: if (!foundtoken) break; Darkvater@1739: Darkvater@1746: if (longtoken) { Darkvater@1746: tokenstream[tstream_i] = *cmdptr; Darkvater@1746: } else { Darkvater@1746: tokenstream[tstream_i] = '\0'; Darkvater@1746: foundtoken = false; Darkvater@1746: } Darkvater@1739: Darkvater@1739: tstream_i++; Darkvater@1739: break; Darkvater@1739: case '"': /* Tokens enclosed in "" are one token */ Darkvater@1739: longtoken = !longtoken; Darkvater@1739: break; Darkvater@1739: default: /* Normal character */ Darkvater@1739: tokenstream[tstream_i++] = *cmdptr; Darkvater@1739: Darkvater@1739: if (!foundtoken) { Darkvater@1739: tokens[t_index++] = &tokenstream[tstream_i - 1]; Darkvater@1739: foundtoken = true; darkvater@135: } Darkvater@1739: break; signde@220: } darkvater@289: } signde@220: Darkvater@1739: if (_stdlib_con_developer) { Darkvater@1739: uint i; Darkvater@1739: for (i = 0; tokens[i] != NULL; i++) Darkvater@1805: IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]); darkvater@135: } darkvater@135: Darkvater@1828: if (tokens[0] == '\0') return; // don't execute empty commands Darkvater@1739: /* 2. Determine type of command (cmd, alias or variable) and execute Darkvater@1739: * First try commands, then aliases, and finally variables. Execute Darkvater@1739: * the found action taking into account its hooking code Darkvater@1739: */ Darkvater@1739: cmd = IConsoleCmdGet(tokens[0]); Darkvater@1739: if (cmd != NULL) { Darkvater@1739: if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) { Darkvater@1739: IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION); Darkvater@1739: if (cmd->proc(t_index, tokens)) { // index started with 0 Darkvater@1739: IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION); Darkvater@1739: } else cmd->proc(0, NULL); // if command failed, give help Darkvater@1739: } Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: t_index--; // ignore the variable-name for comfort for both aliases and variaables Darkvater@1739: alias = IConsoleAliasGet(tokens[0]); Darkvater@1739: if (alias != NULL) { Darkvater@1739: IConsoleAliasExec(alias, t_index, &tokens[1]); Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: var = IConsoleVarGet(tokens[0]); Darkvater@1739: if (var != NULL) { Darkvater@1833: if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) Darkvater@1739: IConsoleVarExec(var, t_index, &tokens[1]); Darkvater@1833: Darkvater@1739: return; Darkvater@1739: } Darkvater@1739: Darkvater@1739: IConsoleError("command or variable not found"); darkvater@135: }