src/console.cpp
branchcustombridgeheads
changeset 5649 55c8267c933f
parent 5643 3778051e8095
child 5650 aefc131bf5ce
equal deleted inserted replaced
5648:1608018c5ff2 5649:55c8267c933f
       
     1 /* $Id$ */
       
     2 
       
     3 #include "stdafx.h"
       
     4 #include "openttd.h"
       
     5 #include "table/strings.h"
       
     6 #include "functions.h"
       
     7 #include "window.h"
       
     8 #include "gui.h"
       
     9 #include "gfx.h"
       
    10 #include "player.h"
       
    11 #include "variables.h"
       
    12 #include "string.h"
       
    13 #include <stdarg.h>
       
    14 #include <string.h>
       
    15 #include "console.h"
       
    16 #include "network/network.h"
       
    17 #include "network/network_data.h"
       
    18 #include "network/network_server.h"
       
    19 
       
    20 #define ICON_BUFFER 79
       
    21 #define ICON_HISTORY_SIZE 20
       
    22 #define ICON_LINE_HEIGHT 12
       
    23 #define ICON_RIGHT_BORDERWIDTH 10
       
    24 #define ICON_BOTTOM_BORDERWIDTH 12
       
    25 #define ICON_MAX_ALIAS_LINES 40
       
    26 #define ICON_TOKEN_COUNT 20
       
    27 
       
    28 // ** main console ** //
       
    29 static char *_iconsole_buffer[ICON_BUFFER + 1];
       
    30 static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
       
    31 static Textbuf _iconsole_cmdline;
       
    32 
       
    33 // ** stdlib ** //
       
    34 byte _stdlib_developer = 1;
       
    35 bool _stdlib_con_developer = false;
       
    36 FILE *_iconsole_output_file;
       
    37 
       
    38 // ** main console cmd buffer
       
    39 static char *_iconsole_history[ICON_HISTORY_SIZE];
       
    40 static byte _iconsole_historypos;
       
    41 
       
    42 /* *************** */
       
    43 /*  end of header  */
       
    44 /* *************** */
       
    45 
       
    46 static void IConsoleClearCommand(void)
       
    47 {
       
    48 	memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
       
    49 	_iconsole_cmdline.length = 0;
       
    50 	_iconsole_cmdline.width = 0;
       
    51 	_iconsole_cmdline.caretpos = 0;
       
    52 	_iconsole_cmdline.caretxoffs = 0;
       
    53 	SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
       
    54 }
       
    55 
       
    56 static inline void IConsoleResetHistoryPos(void) {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
       
    57 
       
    58 
       
    59 static void IConsoleHistoryAdd(const char *cmd);
       
    60 static void IConsoleHistoryNavigate(int direction);
       
    61 
       
    62 // ** console window ** //
       
    63 static void IConsoleWndProc(Window *w, WindowEvent *e)
       
    64 {
       
    65 	static byte iconsole_scroll = ICON_BUFFER;
       
    66 
       
    67 	switch (e->event) {
       
    68 		case WE_PAINT: {
       
    69 			int i = iconsole_scroll;
       
    70 			int max = (w->height / ICON_LINE_HEIGHT) - 1;
       
    71 			int delta = 0;
       
    72 			GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
       
    73 			while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
       
    74 				DoDrawString(_iconsole_buffer[i], 5,
       
    75 					w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
       
    76 				i--;
       
    77 			}
       
    78 			/* If the text is longer than the window, don't show the starting ']' */
       
    79 			delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
       
    80 			if (delta > 0) {
       
    81 				DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
       
    82 				delta = 0;
       
    83 			}
       
    84 
       
    85 			DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
       
    86 
       
    87 			if (_iconsole_cmdline.caret)
       
    88 				DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, 12);
       
    89 			break;
       
    90 		}
       
    91 		case WE_MOUSELOOP:
       
    92 			if (HandleCaret(&_iconsole_cmdline))
       
    93 				SetWindowDirty(w);
       
    94 			break;
       
    95 		case WE_DESTROY:
       
    96 			_iconsole_mode = ICONSOLE_CLOSED;
       
    97 			break;
       
    98 		case WE_KEYPRESS:
       
    99 			e->we.keypress.cont = false;
       
   100 			switch (e->we.keypress.keycode) {
       
   101 				case WKC_UP:
       
   102 					IConsoleHistoryNavigate(+1);
       
   103 					SetWindowDirty(w);
       
   104 					break;
       
   105 				case WKC_DOWN:
       
   106 					IConsoleHistoryNavigate(-1);
       
   107 					SetWindowDirty(w);
       
   108 					break;
       
   109 				case WKC_SHIFT | WKC_PAGEUP:
       
   110 					if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) {
       
   111 						iconsole_scroll = 0;
       
   112 					} else {
       
   113 						iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
       
   114 					}
       
   115 					SetWindowDirty(w);
       
   116 					break;
       
   117 				case WKC_SHIFT | WKC_PAGEDOWN:
       
   118 					if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) {
       
   119 						iconsole_scroll = ICON_BUFFER;
       
   120 					} else {
       
   121 						iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
       
   122 					}
       
   123 					SetWindowDirty(w);
       
   124 					break;
       
   125 				case WKC_SHIFT | WKC_UP:
       
   126 					if (iconsole_scroll <= 0) {
       
   127 						iconsole_scroll = 0;
       
   128 					} else {
       
   129 						--iconsole_scroll;
       
   130 					}
       
   131 					SetWindowDirty(w);
       
   132 					break;
       
   133 				case WKC_SHIFT | WKC_DOWN:
       
   134 					if (iconsole_scroll >= ICON_BUFFER) {
       
   135 						iconsole_scroll = ICON_BUFFER;
       
   136 					} else {
       
   137 						++iconsole_scroll;
       
   138 					}
       
   139 					SetWindowDirty(w);
       
   140 					break;
       
   141 				case WKC_BACKQUOTE:
       
   142 					IConsoleSwitch();
       
   143 					break;
       
   144 				case WKC_RETURN: case WKC_NUM_ENTER:
       
   145 					IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf);
       
   146 					IConsoleHistoryAdd(_iconsole_cmdline.buf);
       
   147 
       
   148 					IConsoleCmdExec(_iconsole_cmdline.buf);
       
   149 					IConsoleClearCommand();
       
   150 					break;
       
   151 				case WKC_CTRL | WKC_RETURN:
       
   152 					_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
       
   153 					IConsoleResize(w);
       
   154 					MarkWholeScreenDirty();
       
   155 					break;
       
   156 				case (WKC_CTRL | 'V'):
       
   157 					if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
       
   158 						IConsoleResetHistoryPos();
       
   159 						SetWindowDirty(w);
       
   160 					}
       
   161 					break;
       
   162 				case (WKC_CTRL | 'L'):
       
   163 					IConsoleCmdExec("clear");
       
   164 					break;
       
   165 				case (WKC_CTRL | 'U'):
       
   166 					DeleteTextBufferAll(&_iconsole_cmdline);
       
   167 					SetWindowDirty(w);
       
   168 					break;
       
   169 				case WKC_BACKSPACE: case WKC_DELETE:
       
   170 					if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) {
       
   171 						IConsoleResetHistoryPos();
       
   172 						SetWindowDirty(w);
       
   173 					}
       
   174 					break;
       
   175 				case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
       
   176 					if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) {
       
   177 						IConsoleResetHistoryPos();
       
   178 						SetWindowDirty(w);
       
   179 					}
       
   180 					break;
       
   181 				default:
       
   182 					if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
       
   183 						iconsole_scroll = ICON_BUFFER;
       
   184 						InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
       
   185 						IConsoleResetHistoryPos();
       
   186 						SetWindowDirty(w);
       
   187 					} else {
       
   188 						e->we.keypress.cont = true;
       
   189 					}
       
   190 			break;
       
   191 		}
       
   192 	}
       
   193 }
       
   194 
       
   195 static const Widget _iconsole_window_widgets[] = {
       
   196 	{WIDGETS_END}
       
   197 };
       
   198 
       
   199 static const WindowDesc _iconsole_window_desc = {
       
   200 	0, 0, 2, 2,
       
   201 	WC_CONSOLE, 0,
       
   202 	WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
       
   203 	_iconsole_window_widgets,
       
   204 	IConsoleWndProc,
       
   205 };
       
   206 
       
   207 void IConsoleInit(void)
       
   208 {
       
   209 	extern const char _openttd_revision[];
       
   210 	_iconsole_output_file = NULL;
       
   211 	_icolour_def  =  1;
       
   212 	_icolour_err  =  3;
       
   213 	_icolour_warn = 13;
       
   214 	_icolour_dbg  =  5;
       
   215 	_icolour_cmd  =  2;
       
   216 	_iconsole_historypos = ICON_HISTORY_SIZE - 1;
       
   217 	_iconsole_mode = ICONSOLE_CLOSED;
       
   218 
       
   219 #ifdef ENABLE_NETWORK /* Initialize network only variables */
       
   220 	_redirect_console_to_client = 0;
       
   221 #endif
       
   222 
       
   223 	memset(_iconsole_history, 0, sizeof(_iconsole_history));
       
   224 	memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer));
       
   225 	memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer));
       
   226 	_iconsole_cmdline.buf = calloc(ICON_CMDLN_SIZE, sizeof(*_iconsole_cmdline.buf)); // create buffer and zero it
       
   227 	_iconsole_cmdline.maxlength = ICON_CMDLN_SIZE;
       
   228 
       
   229 	IConsolePrintF(13, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
       
   230 	IConsolePrint(12,  "------------------------------------");
       
   231 	IConsolePrint(12,  "use \"help\" for more information");
       
   232 	IConsolePrint(12,  "");
       
   233 	IConsoleStdLibRegister();
       
   234 	IConsoleClearCommand();
       
   235 	IConsoleHistoryAdd("");
       
   236 }
       
   237 
       
   238 void IConsoleClearBuffer(void)
       
   239 {
       
   240 	uint i;
       
   241 	for (i = 0; i <= ICON_BUFFER; i++) {
       
   242 		free(_iconsole_buffer[i]);
       
   243 		_iconsole_buffer[i] = NULL;
       
   244 	}
       
   245 }
       
   246 
       
   247 static void IConsoleClear(void)
       
   248 {
       
   249 	free(_iconsole_cmdline.buf);
       
   250 	IConsoleClearBuffer();
       
   251 }
       
   252 
       
   253 static void IConsoleWriteToLogFile(const char *string)
       
   254 {
       
   255 	if (_iconsole_output_file != NULL) {
       
   256 		// if there is an console output file ... also print it there
       
   257 		fwrite(string, strlen(string), 1, _iconsole_output_file);
       
   258 		fwrite("\n", 1, 1, _iconsole_output_file);
       
   259 	}
       
   260 }
       
   261 
       
   262 bool CloseConsoleLogIfActive(void)
       
   263 {
       
   264 	if (_iconsole_output_file != NULL) {
       
   265 		IConsolePrintF(_icolour_def, "file output complete");
       
   266 		fclose(_iconsole_output_file);
       
   267 		_iconsole_output_file = NULL;
       
   268 		return true;
       
   269 	}
       
   270 
       
   271 	return false;
       
   272 }
       
   273 
       
   274 void IConsoleFree(void)
       
   275 {
       
   276 	IConsoleClear();
       
   277 	CloseConsoleLogIfActive();
       
   278 }
       
   279 
       
   280 void IConsoleResize(Window *w)
       
   281 {
       
   282 	switch (_iconsole_mode) {
       
   283 		case ICONSOLE_OPENED:
       
   284 			w->height = _screen.height / 3;
       
   285 			w->width = _screen.width;
       
   286 			break;
       
   287 		case ICONSOLE_FULL:
       
   288 			w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
       
   289 			w->width = _screen.width;
       
   290 			break;
       
   291 		default: return;
       
   292 	}
       
   293 
       
   294 	MarkWholeScreenDirty();
       
   295 }
       
   296 
       
   297 void IConsoleSwitch(void)
       
   298 {
       
   299 	switch (_iconsole_mode) {
       
   300 		case ICONSOLE_CLOSED: {
       
   301 			Window *w = AllocateWindowDesc(&_iconsole_window_desc);
       
   302 			w->height = _screen.height / 3;
       
   303 			w->width = _screen.width;
       
   304 			_iconsole_mode = ICONSOLE_OPENED;
       
   305 			SETBIT(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll
       
   306 		} break;
       
   307 		case ICONSOLE_OPENED: case ICONSOLE_FULL:
       
   308 			DeleteWindowById(WC_CONSOLE, 0);
       
   309 			_iconsole_mode = ICONSOLE_CLOSED;
       
   310 			CLRBIT(_no_scroll, SCROLL_CON);
       
   311 			break;
       
   312 	}
       
   313 
       
   314 	MarkWholeScreenDirty();
       
   315 }
       
   316 
       
   317 void IConsoleClose(void) {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
       
   318 void IConsoleOpen(void)  {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();}
       
   319 
       
   320 /**
       
   321  * Add the entered line into the history so you can look it back
       
   322  * scroll, etc. Put it to the beginning as it is the latest text
       
   323  * @param cmd Text to be entered into the 'history'
       
   324  */
       
   325 static void IConsoleHistoryAdd(const char *cmd)
       
   326 {
       
   327 	free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
       
   328 
       
   329 	memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
       
   330 	_iconsole_history[0] = strdup(cmd);
       
   331 	IConsoleResetHistoryPos();
       
   332 }
       
   333 
       
   334 /**
       
   335  * Navigate Up/Down in the history of typed commands
       
   336  * @param direction Go further back in history (+1), go to recently typed commands (-1)
       
   337  */
       
   338 static void IConsoleHistoryNavigate(int direction)
       
   339 {
       
   340 	int i = _iconsole_historypos + direction;
       
   341 
       
   342 	// watch out for overflows, just wrap around
       
   343 	if (i < 0) i = ICON_HISTORY_SIZE - 1;
       
   344 	if (i >= ICON_HISTORY_SIZE) i = 0;
       
   345 
       
   346 	if (direction > 0)
       
   347 		if (_iconsole_history[i] == NULL) i = 0;
       
   348 
       
   349 	if (direction < 0) {
       
   350 		while (i > 0 && _iconsole_history[i] == NULL) i--;
       
   351 	}
       
   352 
       
   353 	_iconsole_historypos = i;
       
   354 	IConsoleClearCommand();
       
   355 	// copy history to 'command prompt / bash'
       
   356 	assert(_iconsole_history[i] != NULL && IS_INT_INSIDE(i, 0, ICON_HISTORY_SIZE));
       
   357 	ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength);
       
   358 	UpdateTextBufferSize(&_iconsole_cmdline);
       
   359 }
       
   360 
       
   361 /**
       
   362  * Handle the printing of text entered into the console or redirected there
       
   363  * by any other means. Text can be redirected to other players in a network game
       
   364  * as well as to a logfile. If the network server is a dedicated server, all activities
       
   365  * are also logged. All lines to print are added to a temporary buffer which can be
       
   366  * used as a history to print them onscreen
       
   367  * @param color_code the colour of the command. Red in case of errors, etc.
       
   368  * @param string the message entered or output on the console (notice, error, etc.)
       
   369  */
       
   370 void IConsolePrint(uint16 color_code, const char *string)
       
   371 {
       
   372 	char *str;
       
   373 #ifdef ENABLE_NETWORK
       
   374 	if (_redirect_console_to_client != 0) {
       
   375 		/* Redirect the string to the client */
       
   376 		SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string);
       
   377 		return;
       
   378 	}
       
   379 #endif
       
   380 
       
   381 	/* Create a copy of the string, strip if of colours and invalid
       
   382 	 * characters and (when applicable) assign it to the console buffer */
       
   383 	str = strdup(string);
       
   384 	str_strip_colours(str);
       
   385 	str_validate(str);
       
   386 
       
   387 	if (_network_dedicated) {
       
   388 		printf("%s\n", str);
       
   389 		IConsoleWriteToLogFile(str);
       
   390 		free(str); // free duplicated string since it's not used anymore
       
   391 		return;
       
   392 	}
       
   393 
       
   394 	/* move up all the strings in the buffer one place and do the same for colour
       
   395 	 * to accomodate for the new command/message */
       
   396 	free(_iconsole_buffer[0]);
       
   397 	memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER);
       
   398 	_iconsole_buffer[ICON_BUFFER] = str;
       
   399 
       
   400 	memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER);
       
   401 	_iconsole_cbuffer[ICON_BUFFER] = color_code;
       
   402 
       
   403 	IConsoleWriteToLogFile(_iconsole_buffer[ICON_BUFFER]);
       
   404 
       
   405 	SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
       
   406 }
       
   407 
       
   408 /**
       
   409  * Handle the printing of text entered into the console or redirected there
       
   410  * by any other means. Uses printf() style format, for more information look
       
   411  * at @IConsolePrint()
       
   412  */
       
   413 void CDECL IConsolePrintF(uint16 color_code, const char *s, ...)
       
   414 {
       
   415 	va_list va;
       
   416 	char buf[ICON_MAX_STREAMSIZE];
       
   417 
       
   418 	va_start(va, s);
       
   419 	vsnprintf(buf, sizeof(buf), s, va);
       
   420 	va_end(va);
       
   421 
       
   422 	IConsolePrint(color_code, buf);
       
   423 }
       
   424 
       
   425 /**
       
   426  * It is possible to print debugging information to the console,
       
   427  * which is achieved by using this function. Can only be used by
       
   428  * @debug() in debug.c. You need at least a level 2 (developer) for debugging
       
   429  * messages to show up
       
   430  * @param dbg debugging category
       
   431  * @param string debugging message
       
   432  */
       
   433 void IConsoleDebug(const char *dbg, const char *string)
       
   434 {
       
   435 	if (_stdlib_developer > 1)
       
   436 		IConsolePrintF(_icolour_dbg, "dbg: [%s] %s", dbg, string);
       
   437 }
       
   438 
       
   439 /**
       
   440  * It is possible to print warnings to the console. These are mostly
       
   441  * errors or mishaps, but non-fatal. You need at least a level 1 (developer) for
       
   442  * debugging messages to show up
       
   443  */
       
   444 void IConsoleWarning(const char *string)
       
   445 {
       
   446 	if (_stdlib_developer > 0)
       
   447 		IConsolePrintF(_icolour_warn, "WARNING: %s", string);
       
   448 }
       
   449 
       
   450 /**
       
   451  * It is possible to print error information to the console. This can include
       
   452  * game errors, or errors in general you would want the user to notice
       
   453  */
       
   454 void IConsoleError(const char *string)
       
   455 {
       
   456 	IConsolePrintF(_icolour_err, "ERROR: %s", string);
       
   457 }
       
   458 
       
   459 /**
       
   460  * Change a string into its number representation. Supports
       
   461  * decimal and hexadecimal numbers as well as 'on'/'off' 'true'/'false'
       
   462  * @param *value the variable a successfull conversion will be put in
       
   463  * @param *arg the string to be converted
       
   464  * @return Return true on success or false on failure
       
   465  */
       
   466 bool GetArgumentInteger(uint32 *value, const char *arg)
       
   467 {
       
   468 	char *endptr;
       
   469 
       
   470 	if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
       
   471 		*value = 1;
       
   472 		return true;
       
   473 	}
       
   474 	if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
       
   475 		*value = 0;
       
   476 		return true;
       
   477 	}
       
   478 
       
   479 	*value = strtoul(arg, &endptr, 0);
       
   480 	return arg != endptr;
       
   481 }
       
   482 
       
   483 // * ************************* * //
       
   484 // * hooking code              * //
       
   485 // * ************************* * //
       
   486 /**
       
   487  * General internal hooking code that is the same for both commands and variables
       
   488  * @param hooks @IConsoleHooks structure that will be set according to
       
   489  * @param type type access trigger
       
   490  * @param proc function called when the hook criteria is met
       
   491  */
       
   492 static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc)
       
   493 {
       
   494 	if (hooks == NULL || proc == NULL) return;
       
   495 
       
   496 	switch (type) {
       
   497 		case ICONSOLE_HOOK_ACCESS:
       
   498 			hooks->access = proc;
       
   499 			break;
       
   500 		case ICONSOLE_HOOK_PRE_ACTION:
       
   501 			hooks->pre = proc;
       
   502 			break;
       
   503 		case ICONSOLE_HOOK_POST_ACTION:
       
   504 			hooks->post = proc;
       
   505 			break;
       
   506 		default: NOT_REACHED();
       
   507 	}
       
   508 }
       
   509 
       
   510 /**
       
   511  * Handle any special hook triggers. If the hook type is met check if
       
   512  * there is a function associated with that and if so, execute it
       
   513  * @param hooks @IConsoleHooks structure that will be checked
       
   514  * @param type type of hook, trigger that needs to be activated
       
   515  * @return true on a successfull execution of the hook command or if there
       
   516  * is no hook/trigger present at all. False otherwise
       
   517  */
       
   518 static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type)
       
   519 {
       
   520 	IConsoleHook *proc = NULL;
       
   521 	if (hooks == NULL) return false;
       
   522 
       
   523 	switch (type) {
       
   524 		case ICONSOLE_HOOK_ACCESS:
       
   525 			proc = hooks->access;
       
   526 			break;
       
   527 		case ICONSOLE_HOOK_PRE_ACTION:
       
   528 			proc = hooks->pre;
       
   529 			break;
       
   530 		case ICONSOLE_HOOK_POST_ACTION:
       
   531 			proc = hooks->post;
       
   532 			break;
       
   533 		default: NOT_REACHED();
       
   534 	}
       
   535 
       
   536 	return (proc == NULL) ? true : proc();
       
   537 }
       
   538 
       
   539 /**
       
   540  * Add a hook to a command that will be triggered at certain points
       
   541  * @param name name of the command that the hook is added to
       
   542  * @param type type of hook that is added (ACCESS, BEFORE and AFTER change)
       
   543  * @param proc function called when the hook criteria is met
       
   544  */
       
   545 void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
       
   546 {
       
   547 	IConsoleCmd *cmd = IConsoleCmdGet(name);
       
   548 	if (cmd == NULL) return;
       
   549 	IConsoleHookAdd(&cmd->hook, type, proc);
       
   550 }
       
   551 
       
   552 /**
       
   553  * Add a hook to a variable that will be triggered at certain points
       
   554  * @param name name of the variable that the hook is added to
       
   555  * @param type type of hook that is added (ACCESS, BEFORE and AFTER change)
       
   556  * @param proc function called when the hook criteria is met
       
   557  */
       
   558 void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
       
   559 {
       
   560 	IConsoleVar *var = IConsoleVarGet(name);
       
   561 	if (var == NULL) return;
       
   562 	IConsoleHookAdd(&var->hook, type, proc);
       
   563 }
       
   564 
       
   565 /**
       
   566  * Perhaps ugly macro, but this saves us the trouble of writing the same function
       
   567  * three types, just with different variables. Yes, templates would be handy. It was
       
   568  * either this define or an even more ugly void* magic function
       
   569  */
       
   570 #define IConsoleAddSorted(_base, item_new, IConsoleType, type)                 \
       
   571 {                                                                              \
       
   572 	IConsoleType *item, *item_before;                                            \
       
   573 	/* first command */                                                          \
       
   574 	if (_base == NULL) {                                                         \
       
   575 		_base = item_new;                                                          \
       
   576 		return;                                                                    \
       
   577 	}                                                                            \
       
   578                                                                                \
       
   579 	item_before = NULL;                                                          \
       
   580 	item = _base;                                                                \
       
   581                                                                                \
       
   582 	/* BEGIN - Alphabetically insert the commands into the linked list */        \
       
   583 	while (item != NULL) {                                                       \
       
   584 		int i = strcmp(item->name, item_new->name);                                \
       
   585 		if (i == 0) {                                                              \
       
   586 			IConsoleError(type " with this name already exists; insertion aborted"); \
       
   587 			free(item_new);                                                          \
       
   588 			return;                                                                  \
       
   589 		}                                                                          \
       
   590                                                                                \
       
   591 		if (i > 0) break; /* insert at this position */                            \
       
   592                                                                                \
       
   593 		item_before = item;                                                        \
       
   594 		item = item->next;                                                         \
       
   595 	}                                                                            \
       
   596                                                                                \
       
   597 	if (item_before == NULL) {                                                   \
       
   598 		_base = item_new;                                                          \
       
   599 	} else {                                                                     \
       
   600 		item_before->next = item_new;                                              \
       
   601   }                                                                            \
       
   602                                                                                \
       
   603 	item_new->next = item;                                                       \
       
   604 	/* END - Alphabetical insert */                                              \
       
   605 }
       
   606 
       
   607 /**
       
   608  * Register a new command to be used in the console
       
   609  * @param name name of the command that will be used
       
   610  * @param proc function that will be called upon execution of command
       
   611  */
       
   612 void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc)
       
   613 {
       
   614 	char *new_cmd = strdup(name);
       
   615 	IConsoleCmd *item_new = malloc(sizeof(IConsoleCmd));
       
   616 
       
   617 	item_new->next = NULL;
       
   618 	item_new->proc = proc;
       
   619 	item_new->name = new_cmd;
       
   620 
       
   621 	item_new->hook.access = NULL;
       
   622 	item_new->hook.pre = NULL;
       
   623 	item_new->hook.post = NULL;
       
   624 
       
   625 	IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command");
       
   626 }
       
   627 
       
   628 /**
       
   629  * Find the command pointed to by its string
       
   630  * @param name command to be found
       
   631  * @return return Cmdstruct of the found command, or NULL on failure
       
   632  */
       
   633 IConsoleCmd *IConsoleCmdGet(const char *name)
       
   634 {
       
   635 	IConsoleCmd *item;
       
   636 
       
   637 	for (item = _iconsole_cmds; item != NULL; item = item->next) {
       
   638 		if (strcmp(item->name, name) == 0) return item;
       
   639 	}
       
   640 	return NULL;
       
   641 }
       
   642 
       
   643 /**
       
   644  * Register a an alias for an already existing command in the console
       
   645  * @param name name of the alias that will be used
       
   646  * @param cmd name of the command that 'name' will be alias of
       
   647  */
       
   648 void IConsoleAliasRegister(const char *name, const char *cmd)
       
   649 {
       
   650 	char *new_alias = strdup(name);
       
   651 	char *cmd_aliased = strdup(cmd);
       
   652 	IConsoleAlias *item_new = malloc(sizeof(IConsoleAlias));
       
   653 
       
   654 	item_new->next = NULL;
       
   655 	item_new->cmdline = cmd_aliased;
       
   656 	item_new->name = new_alias;
       
   657 
       
   658 	IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias");
       
   659 }
       
   660 
       
   661 /**
       
   662  * Find the alias pointed to by its string
       
   663  * @param name alias to be found
       
   664  * @return return Aliasstruct of the found alias, or NULL on failure
       
   665  */
       
   666 IConsoleAlias *IConsoleAliasGet(const char *name)
       
   667 {
       
   668 	IConsoleAlias* item;
       
   669 
       
   670 	for (item = _iconsole_aliases; item != NULL; item = item->next) {
       
   671 		if (strcmp(item->name, name) == 0) return item;
       
   672 	}
       
   673 
       
   674 	return NULL;
       
   675 }
       
   676 
       
   677 /** copy in an argument into the aliasstream */
       
   678 static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
       
   679 {
       
   680 	int len = min(ICON_MAX_STREAMSIZE - bufpos, (uint)strlen(src));
       
   681 	strncpy(dst, src, len);
       
   682 
       
   683 	return len;
       
   684 }
       
   685 
       
   686 /**
       
   687  * An alias is just another name for a command, or for more commands
       
   688  * Execute it as well.
       
   689  * @param *alias is the alias of the command
       
   690  * @param tokencount the number of parameters passed
       
   691  * @param *tokens are the parameters given to the original command (0 is the first param)
       
   692  */
       
   693 static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
       
   694 {
       
   695 	const char *cmdptr;
       
   696 	char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
       
   697 	uint i;
       
   698 	uint a_index, astream_i;
       
   699 
       
   700 	memset(&aliases, 0, sizeof(aliases));
       
   701 	memset(&aliasstream, 0, sizeof(aliasstream));
       
   702 
       
   703 	if (_stdlib_con_developer)
       
   704 		IConsolePrintF(_icolour_dbg, "condbg: requested command is an alias; parsing...");
       
   705 
       
   706 	aliases[0] = aliasstream;
       
   707 	for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
       
   708 		if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
       
   709 
       
   710 		switch (*cmdptr) {
       
   711 		case '\'': /* ' will double for "" */
       
   712 			aliasstream[astream_i++] = '"';
       
   713 			break;
       
   714 		case ';': /* Cmd seperator, start new command */
       
   715 			aliasstream[astream_i] = '\0';
       
   716 			aliases[++a_index] = &aliasstream[++astream_i];
       
   717 			cmdptr++;
       
   718 			break;
       
   719 		case '%': /* Some or all parameters */
       
   720 			cmdptr++;
       
   721 			switch (*cmdptr) {
       
   722 			case '+': { /* All parameters seperated: "[param 1]" "[param 2]" */
       
   723 				for (i = 0; i != tokencount; i++) {
       
   724 					aliasstream[astream_i++] = '"';
       
   725 					astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
       
   726 					aliasstream[astream_i++] = '"';
       
   727 					aliasstream[astream_i++] = ' ';
       
   728 				}
       
   729 			} break;
       
   730 			case '!': { /* Merge the parameters to one: "[param 1] [param 2] [param 3...]" */
       
   731 				aliasstream[astream_i++] = '"';
       
   732 				for (i = 0; i != tokencount; i++) {
       
   733 					astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
       
   734 					aliasstream[astream_i++] = ' ';
       
   735 				}
       
   736 				aliasstream[astream_i++] = '"';
       
   737 
       
   738 			} break;
       
   739 				default: { /* One specific parameter: %A = [param 1] %B = [param 2] ... */
       
   740 				int param = *cmdptr - 'A';
       
   741 
       
   742 				if (param < 0 || param >= tokencount) {
       
   743 					IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
       
   744 					IConsolePrintF(_icolour_warn, "Usage of alias '%s': %s", alias->name, alias->cmdline);
       
   745 					return;
       
   746 				}
       
   747 
       
   748 				aliasstream[astream_i++] = '"';
       
   749 				astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
       
   750 				aliasstream[astream_i++] = '"';
       
   751 			} break;
       
   752 			} break;
       
   753 
       
   754 		default:
       
   755 			aliasstream[astream_i++] = *cmdptr;
       
   756 			break;
       
   757 		}
       
   758 	}
       
   759 
       
   760 	for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
       
   761 }
       
   762 
       
   763 /**
       
   764  * Special function for adding string-type variables. They in addition
       
   765  * also need a 'size' value saying how long their string buffer is.
       
   766  * @param size the length of the string buffer
       
   767  * For more information see @IConsoleVarRegister()
       
   768  */
       
   769 void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help)
       
   770 {
       
   771 	IConsoleVar *var;
       
   772 	IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help);
       
   773 	var = IConsoleVarGet(name);
       
   774 	var->size = size;
       
   775 }
       
   776 
       
   777 /**
       
   778  * Register a new variable to be used in the console
       
   779  * @param name name of the variable that will be used
       
   780  * @param addr memory location the variable will point to
       
   781  * @param help the help string shown for the variable
       
   782  * @param type the type of the variable (simple atomic) so we know which values it can get
       
   783  */
       
   784 void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help)
       
   785 {
       
   786 	char *new_cmd = strdup(name);
       
   787 	IConsoleVar *item_new = malloc(sizeof(IConsoleVar));
       
   788 
       
   789 	item_new->help = (help != NULL) ? strdup(help) : NULL;
       
   790 
       
   791 	item_new->next = NULL;
       
   792 	item_new->name = new_cmd;
       
   793 	item_new->addr = addr;
       
   794 	item_new->proc = NULL;
       
   795 	item_new->type = type;
       
   796 
       
   797 	item_new->hook.access = NULL;
       
   798 	item_new->hook.pre = NULL;
       
   799 	item_new->hook.post = NULL;
       
   800 
       
   801 	IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable");
       
   802 }
       
   803 
       
   804 /**
       
   805  * Find the variable pointed to by its string
       
   806  * @param name variable to be found
       
   807  * @return return Varstruct of the found variable, or NULL on failure
       
   808  */
       
   809 IConsoleVar *IConsoleVarGet(const char *name)
       
   810 {
       
   811 	IConsoleVar *item;
       
   812 	for (item = _iconsole_vars; item != NULL; item = item->next) {
       
   813 		if (strcmp(item->name, name) == 0) return item;
       
   814 	}
       
   815 
       
   816 	return NULL;
       
   817 }
       
   818 
       
   819 /**
       
   820  * Set a new value to a console variable
       
   821  * @param *var the variable being set/changed
       
   822  * @param value the new value given to the variable, cast properly
       
   823  */
       
   824 static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
       
   825 {
       
   826 	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
       
   827 	switch (var->type) {
       
   828 		case ICONSOLE_VAR_BOOLEAN:
       
   829 			*(bool*)var->addr = (value != 0);
       
   830 			break;
       
   831 		case ICONSOLE_VAR_BYTE:
       
   832 			*(byte*)var->addr = (byte)value;
       
   833 			break;
       
   834 		case ICONSOLE_VAR_UINT16:
       
   835 			*(uint16*)var->addr = (uint16)value;
       
   836 			break;
       
   837 		case ICONSOLE_VAR_INT16:
       
   838 			*(int16*)var->addr = (int16)value;
       
   839 			break;
       
   840 		case ICONSOLE_VAR_UINT32:
       
   841 			*(uint32*)var->addr = (uint32)value;
       
   842 			break;
       
   843 		case ICONSOLE_VAR_INT32:
       
   844 			*(int32*)var->addr = (int32)value;
       
   845 			break;
       
   846 		default: NOT_REACHED();
       
   847 	}
       
   848 
       
   849 	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
       
   850 	IConsoleVarPrintSetValue(var);
       
   851 }
       
   852 
       
   853 /**
       
   854  * Set a new value to a string-type variable. Basically this
       
   855  * means to copy the new value over to the container.
       
   856  * @param *var the variable in question
       
   857  * @param *value the new value
       
   858  */
       
   859 static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
       
   860 {
       
   861 	if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
       
   862 
       
   863 	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
       
   864 	ttd_strlcpy(var->addr, value, var->size);
       
   865 	IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
       
   866 	IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
       
   867 	return;
       
   868 }
       
   869 
       
   870 /**
       
   871  * Query the current value of a variable and return it
       
   872  * @param *var the variable queried
       
   873  * @return current value of the variable
       
   874  */
       
   875 static uint32 IConsoleVarGetValue(const IConsoleVar *var)
       
   876 {
       
   877 	uint32 result = 0;
       
   878 
       
   879 	switch (var->type) {
       
   880 		case ICONSOLE_VAR_BOOLEAN:
       
   881 			result = *(bool*)var->addr;
       
   882 			break;
       
   883 		case ICONSOLE_VAR_BYTE:
       
   884 			result = *(byte*)var->addr;
       
   885 			break;
       
   886 		case ICONSOLE_VAR_UINT16:
       
   887 			result = *(uint16*)var->addr;
       
   888 			break;
       
   889 		case ICONSOLE_VAR_INT16:
       
   890 			result = *(int16*)var->addr;
       
   891 			break;
       
   892 		case ICONSOLE_VAR_UINT32:
       
   893 			result = *(uint32*)var->addr;
       
   894 			break;
       
   895 		case ICONSOLE_VAR_INT32:
       
   896 			result = *(int32*)var->addr;
       
   897 			break;
       
   898 		default: NOT_REACHED();
       
   899 	}
       
   900 	return result;
       
   901 }
       
   902 
       
   903 /**
       
   904  * Get the value of the variable and put it into a printable
       
   905  * string form so we can use it for printing
       
   906  */
       
   907 static char *IConsoleVarGetStringValue(const IConsoleVar *var)
       
   908 {
       
   909 	static char tempres[50];
       
   910 	char *value = tempres;
       
   911 
       
   912 	switch (var->type) {
       
   913 		case ICONSOLE_VAR_BOOLEAN:
       
   914 			snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off");
       
   915 			break;
       
   916 		case ICONSOLE_VAR_BYTE:
       
   917 			snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr);
       
   918 			break;
       
   919 		case ICONSOLE_VAR_UINT16:
       
   920 			snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr);
       
   921 			break;
       
   922 		case ICONSOLE_VAR_UINT32:
       
   923 			snprintf(tempres, sizeof(tempres), "%u",  *(uint32*)var->addr);
       
   924 			break;
       
   925 		case ICONSOLE_VAR_INT16:
       
   926 			snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr);
       
   927 			break;
       
   928 		case ICONSOLE_VAR_INT32:
       
   929 			snprintf(tempres, sizeof(tempres), "%i",  *(int32*)var->addr);
       
   930 			break;
       
   931 		case ICONSOLE_VAR_STRING:
       
   932 			value = (char*)var->addr;
       
   933 			break;
       
   934 		default: NOT_REACHED();
       
   935 	}
       
   936 
       
   937 	return value;
       
   938 }
       
   939 
       
   940 /**
       
   941  * Print out the value of the variable when asked
       
   942  */
       
   943 void IConsoleVarPrintGetValue(const IConsoleVar *var)
       
   944 {
       
   945 	char *value;
       
   946 	/* Some variables need really specific handling, handle this in its
       
   947 	 * callback function */
       
   948 	if (var->proc != NULL) {
       
   949 		var->proc(0, NULL);
       
   950 		return;
       
   951 	}
       
   952 
       
   953 	value = IConsoleVarGetStringValue(var);
       
   954 	IConsolePrintF(_icolour_warn, "Current value for '%s' is:  %s", var->name, value);
       
   955 }
       
   956 
       
   957 /**
       
   958  * Print out the value of the variable after it has been assigned
       
   959  * a new value, thus giving us feedback on the action
       
   960  */
       
   961 void IConsoleVarPrintSetValue(const IConsoleVar *var)
       
   962 {
       
   963 	char *value = IConsoleVarGetStringValue(var);
       
   964 	IConsolePrintF(_icolour_warn, "'%s' changed to:  %s", var->name, value);
       
   965 }
       
   966 
       
   967 /**
       
   968  * Execute a variable command. Without any parameters, print out its value
       
   969  * with parameters it assigns a new value to the variable
       
   970  * @param *var the variable that we will be querying/changing
       
   971  * @param tokencount how many additional parameters have been given to the commandline
       
   972  * @param *token the actual parameters the variable was called with
       
   973  */
       
   974 void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT])
       
   975 {
       
   976 	const char *tokenptr = token[0];
       
   977 	byte t_index = tokencount;
       
   978 	uint32 value;
       
   979 
       
   980 	if (_stdlib_con_developer)
       
   981 		IConsolePrintF(_icolour_dbg, "condbg: requested command is a variable");
       
   982 
       
   983 	if (tokencount == 0) { /* Just print out value */
       
   984 		IConsoleVarPrintGetValue(var);
       
   985 		return;
       
   986 	}
       
   987 
       
   988 	/* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */
       
   989 	if (strcmp(tokenptr, "=") == 0) tokencount--;
       
   990 
       
   991 	if (tokencount == 1) {
       
   992 		/* Some variables need really special handling, handle it in their callback procedure */
       
   993 		if (var->proc != NULL) {
       
   994 			var->proc(tokencount, &token[t_index - tokencount]); // set the new value
       
   995 			return;
       
   996 		}
       
   997 		/* Strings need special processing. No need to convert the argument to
       
   998 		 * an integer value, just copy over the argument on a one-by-one basis */
       
   999 		if (var->type == ICONSOLE_VAR_STRING) {
       
  1000 			IConsoleVarSetStringvalue(var, token[t_index - tokencount]);
       
  1001 			return;
       
  1002 		} else if (GetArgumentInteger(&value, token[t_index - tokencount])) {
       
  1003 			IConsoleVarSetValue(var, value);
       
  1004 			return;
       
  1005 		}
       
  1006 
       
  1007 		/* Increase or decrease the value by one. This of course can only happen to 'number' types */
       
  1008 		if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) {
       
  1009 			IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1);
       
  1010 			return;
       
  1011 		}
       
  1012 
       
  1013 		if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) {
       
  1014 			IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1);
       
  1015 			return;
       
  1016 		}
       
  1017 	}
       
  1018 
       
  1019 	IConsoleError("invalid variable assignment");
       
  1020 }
       
  1021 
       
  1022 /**
       
  1023  * Add a callback function to the variable. Some variables need
       
  1024  * very special processing, which can only be done with custom code
       
  1025  * @param name name of the variable the callback function is added to
       
  1026  * @param proc the function called
       
  1027  */
       
  1028 void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc)
       
  1029 {
       
  1030 	IConsoleVar *var = IConsoleVarGet(name);
       
  1031 	if (var == NULL) return;
       
  1032 	var->proc = proc;
       
  1033 }
       
  1034 
       
  1035 /**
       
  1036  * Execute a given command passed to us. First chop it up into
       
  1037  * individual tokens (seperated by spaces), then execute it if possible
       
  1038  * @param cmdstr string to be parsed and executed
       
  1039  */
       
  1040 void IConsoleCmdExec(const char *cmdstr)
       
  1041 {
       
  1042 	IConsoleCmd   *cmd    = NULL;
       
  1043 	IConsoleAlias *alias  = NULL;
       
  1044 	IConsoleVar   *var    = NULL;
       
  1045 
       
  1046 	const char *cmdptr;
       
  1047 	char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
       
  1048 	uint t_index, tstream_i;
       
  1049 
       
  1050 	bool longtoken = false;
       
  1051 	bool foundtoken = false;
       
  1052 
       
  1053 	if (cmdstr[0] == '#') return; // comments
       
  1054 
       
  1055 	for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
       
  1056 		if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
       
  1057 			IConsoleError("command contains malformed characters, aborting");
       
  1058 			IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
       
  1059 			return;
       
  1060 		}
       
  1061 	}
       
  1062 
       
  1063 	if (_stdlib_con_developer)
       
  1064 		IConsolePrintF(_icolour_dbg, "condbg: executing cmdline: '%s'", cmdstr);
       
  1065 
       
  1066 	memset(&tokens, 0, sizeof(tokens));
       
  1067 	memset(&tokenstream, 0, sizeof(tokenstream));
       
  1068 
       
  1069 	/* 1. Split up commandline into tokens, seperated by spaces, commands
       
  1070 	 * enclosed in "" are taken as one token. We can only go as far as the amount
       
  1071 	 * of characters in our stream or the max amount of tokens we can handle */
       
  1072 	for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
       
  1073 		if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
       
  1074 
       
  1075 		switch (*cmdptr) {
       
  1076 		case ' ': /* Token seperator */
       
  1077 			if (!foundtoken) break;
       
  1078 
       
  1079 			if (longtoken) {
       
  1080 				tokenstream[tstream_i] = *cmdptr;
       
  1081 			} else {
       
  1082 				tokenstream[tstream_i] = '\0';
       
  1083 				foundtoken = false;
       
  1084 			}
       
  1085 
       
  1086 			tstream_i++;
       
  1087 			break;
       
  1088 		case '"': /* Tokens enclosed in "" are one token */
       
  1089 			longtoken = !longtoken;
       
  1090 			break;
       
  1091 		case '\\': /* Escape character for "" */
       
  1092 			if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
       
  1093 				tokenstream[tstream_i++] = *++cmdptr;
       
  1094 				break;
       
  1095 			}
       
  1096 			/* fallthrough */
       
  1097 		default: /* Normal character */
       
  1098 			tokenstream[tstream_i++] = *cmdptr;
       
  1099 
       
  1100 			if (!foundtoken) {
       
  1101 				tokens[t_index++] = &tokenstream[tstream_i - 1];
       
  1102 				foundtoken = true;
       
  1103 			}
       
  1104 			break;
       
  1105 		}
       
  1106 	}
       
  1107 
       
  1108 	if (_stdlib_con_developer) {
       
  1109 		uint i;
       
  1110 
       
  1111 		for (i = 0; tokens[i] != NULL; i++) {
       
  1112 			IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]);
       
  1113 		}
       
  1114 	}
       
  1115 
       
  1116 	if (tokens[0] == '\0') return; // don't execute empty commands
       
  1117 	/* 2. Determine type of command (cmd, alias or variable) and execute
       
  1118 	 * First try commands, then aliases, and finally variables. Execute
       
  1119 	 * the found action taking into account its hooking code
       
  1120 	 */
       
  1121 	cmd = IConsoleCmdGet(tokens[0]);
       
  1122 	if (cmd != NULL) {
       
  1123 		if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
       
  1124 			IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
       
  1125 			if (cmd->proc(t_index, tokens)) { // index started with 0
       
  1126 				IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
       
  1127 			} else {
       
  1128 				cmd->proc(0, NULL); // if command failed, give help
       
  1129 			}
       
  1130 		}
       
  1131 		return;
       
  1132 	}
       
  1133 
       
  1134 	t_index--; // ignore the variable-name for comfort for both aliases and variaables
       
  1135 	alias = IConsoleAliasGet(tokens[0]);
       
  1136 	if (alias != NULL) {
       
  1137 		IConsoleAliasExec(alias, t_index, &tokens[1]);
       
  1138 		return;
       
  1139 	}
       
  1140 
       
  1141 	var = IConsoleVarGet(tokens[0]);
       
  1142 	if (var != NULL) {
       
  1143 		if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
       
  1144 			IConsoleVarExec(var, t_index, &tokens[1]);
       
  1145 		}
       
  1146 		return;
       
  1147 	}
       
  1148 
       
  1149 	IConsoleError("command or variable not found");
       
  1150 }