# console.cpp

Go to the documentation of this file.

    00001 /* $Id: console.cpp 16273 2009-05-10 21:33:55Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "core/alloc_func.hpp"
    00007 #include "core/math_func.hpp"
    00008 #include "string_func.h"
    00009 #include "console_internal.h"
    00010 #include "network/network.h"
    00011 #include "network/network_func.h"
    00012
    00013 #include <stdarg.h>
    00014
    00015 #define ICON_BUFFER 79
    00016 #define ICON_HISTORY_SIZE 20
    00017 #define ICON_LINE_HEIGHT 12
    00018 #define ICON_RIGHT_BORDERWIDTH 10
    00019 #define ICON_BOTTOM_BORDERWIDTH 12
    00020 #define ICON_MAX_ALIAS_LINES 40
    00021 #define ICON_TOKEN_COUNT 20
    00022
    00023 /* console parser */
    00024 IConsoleCmd   *_iconsole_cmds;
    00025 IConsoleVar   *_iconsole_vars;
    00026 IConsoleAlias *_iconsole_aliases;
    00027
    00028 /* ** stdlib ** */
    00029 byte _stdlib_developer = 1;
    00030 bool _stdlib_con_developer = false;
    00031 FILE *_iconsole_output_file;
    00032
    00033 void IConsoleInit()
    00034 {
    00035   _iconsole_output_file = NULL;
    00036 #ifdef ENABLE_NETWORK /* Initialize network only variables */
    00037   _redirect_console_to_client = INVALID_CLIENT_ID;
    00038 #endif
    00039
    00040   IConsoleGUIInit();
    00041
    00042   IConsoleStdLibRegister();
    00043 }
    00044
    00045 static void IConsoleWriteToLogFile(const char *string)
    00046 {
    00047   if (_iconsole_output_file != NULL) {
    00048     /* if there is an console output file ... also print it there */
    00049     if (fwrite(string, strlen(string), 1, _iconsole_output_file) != 1 ||
    00050         fwrite("\n", 1, 1, _iconsole_output_file) != 1) {
    00051       fclose(_iconsole_output_file);
    00052       _iconsole_output_file = NULL;
    00053       IConsolePrintF(CC_DEFAULT, "cannot write to log file");
    00054     }
    00055   }
    00056 }
    00057
    00058 bool CloseConsoleLogIfActive()
    00059 {
    00060   if (_iconsole_output_file != NULL) {
    00061     IConsolePrintF(CC_DEFAULT, "file output complete");
    00062     fclose(_iconsole_output_file);
    00063     _iconsole_output_file = NULL;
    00064     return true;
    00065   }
    00066
    00067   return false;
    00068 }
    00069
    00070 void IConsoleFree()
    00071 {
    00072   IConsoleGUIFree();
    00073   CloseConsoleLogIfActive();
    00074 }
    00075
    00085 void IConsolePrint(ConsoleColour colour_code, const char *string)
    00086 {
    00087   char *str;
    00088 #ifdef ENABLE_NETWORK
    00089   if (_redirect_console_to_client != INVALID_CLIENT_ID) {
    00090     /* Redirect the string to the client */
    00091     NetworkServerSendRcon(_redirect_console_to_client, colour_code, string);
    00092     return;
    00093   }
    00094 #endif
    00095
    00096   /* Create a copy of the string, strip if of colours and invalid
    00097    * characters and (when applicable) assign it to the console buffer */
    00098   str = strdup(string);
    00099   str_strip_colours(str);
    00100   str_validate(str, str + strlen(str));
    00101
    00102   if (_network_dedicated) {
    00103     fprintf(stdout, "%s\n", str);
    00104     fflush(stdout);
    00105     IConsoleWriteToLogFile(str);
    00106     free(str); // free duplicated string since it's not used anymore
    00107     return;
    00108   }
    00109
    00110   IConsoleWriteToLogFile(str);
    00111   IConsoleGUIPrint(colour_code, str);
    00112 }
    00113
    00119 void CDECL IConsolePrintF(ConsoleColour colour_code, const char *format, ...)
    00120 {
    00121   va_list va;
    00122   char buf[ICON_MAX_STREAMSIZE];
    00123
    00124   va_start(va, format);
    00125   vsnprintf(buf, sizeof(buf), format, va);
    00126   va_end(va);
    00127
    00128   IConsolePrint(colour_code, buf);
    00129 }
    00130
    00139 void IConsoleDebug(const char *dbg, const char *string)
    00140 {
    00141   if (_stdlib_developer > 1)
    00142     IConsolePrintF(CC_DEBUG, "dbg: [%s] %s", dbg, string);
    00143 }
    00144
    00150 void IConsoleWarning(const char *string)
    00151 {
    00152   if (_stdlib_developer > 0)
    00153     IConsolePrintF(CC_WARNING, "WARNING: %s", string);
    00154 }
    00155
    00160 void IConsoleError(const char *string)
    00161 {
    00162   IConsolePrintF(CC_ERROR, "ERROR: %s", string);
    00163 }
    00164
    00172 bool GetArgumentInteger(uint32 *value, const char *arg)
    00173 {
    00174   char *endptr;
    00175
    00176   if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
    00177     *value = 1;
    00178     return true;
    00179   }
    00180   if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
    00181     *value = 0;
    00182     return true;
    00183   }
    00184
    00185   *value = strtoul(arg, &endptr, 0);
    00186   return arg != endptr;
    00187 }
    00188
    00189 /*  * *************************
    00190     * hooking code            *
    00191     * *************************/
    00192
    00199 static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc)
    00200 {
    00201   if (hooks == NULL || proc == NULL) return;
    00202
    00203   switch (type) {
    00204     case ICONSOLE_HOOK_ACCESS:
    00205       hooks->access = proc;
    00206       break;
    00207     case ICONSOLE_HOOK_PRE_ACTION:
    00208       hooks->pre = proc;
    00209       break;
    00210     case ICONSOLE_HOOK_POST_ACTION:
    00211       hooks->post = proc;
    00212       break;
    00213     default: NOT_REACHED();
    00214   }
    00215 }
    00216
    00225 static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type)
    00226 {
    00227   IConsoleHook *proc = NULL;
    00228   if (hooks == NULL) return false;
    00229
    00230   switch (type) {
    00231     case ICONSOLE_HOOK_ACCESS:
    00232       proc = hooks->access;
    00233       break;
    00234     case ICONSOLE_HOOK_PRE_ACTION:
    00235       proc = hooks->pre;
    00236       break;
    00237     case ICONSOLE_HOOK_POST_ACTION:
    00238       proc = hooks->post;
    00239       break;
    00240     default: NOT_REACHED();
    00241   }
    00242
    00243   return (proc == NULL) ? true : proc();
    00244 }
    00245
    00252 void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00253 {
    00254   IConsoleCmd *cmd = IConsoleCmdGet(name);
    00255   if (cmd == NULL) return;
    00256   IConsoleHookAdd(&cmd->hook, type, proc);
    00257 }
    00258
    00265 void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00266 {
    00267   IConsoleVar *var = IConsoleVarGet(name);
    00268   if (var == NULL) return;
    00269   IConsoleHookAdd(&var->hook, type, proc);
    00270 }
    00271
    00277 #define IConsoleAddSorted(_base, item_new, IConsoleType, type)                 \
    00278 {                                                                              \
    00279   IConsoleType *item, *item_before;                                            \
    00280   /* first command */                                                          \
    00281   if (_base == NULL) {                                                         \
    00282     _base = item_new;                                                          \
    00283     return;                                                                    \
    00284   }                                                                            \
    00285                                                                                \
    00286   item_before = NULL;                                                          \
    00287   item = _base;                                                                \
    00288                                                                                \
    00289   /* BEGIN - Alphabetically insert the commands into the linked list */        \
    00290   while (item != NULL) {                                                       \
    00291     int i = strcmp(item->name, item_new->name);                                \
    00292     if (i == 0) {                                                              \
    00293       IConsoleError(type " with this name already exists; insertion aborted"); \
    00294       free(item_new);                                                          \
    00295       return;                                                                  \
    00296     }                                                                          \
    00297                                                                                \
    00298     if (i > 0) break; /* insert at this position */                            \
    00299                                                                                \
    00300     item_before = item;                                                        \
    00301     item = item->next;                                                         \
    00302   }                                                                            \
    00303                                                                                \
    00304   if (item_before == NULL) {                                                   \
    00305     _base = item_new;                                                          \
    00306   } else {                                                                     \
    00307     item_before->next = item_new;                                              \
    00308   }                                                                            \
    00309                                                                                \
    00310   item_new->next = item;                                                       \
    00311   /* END - Alphabetical insert */                                              \
    00312 }
    00313
    00319 void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc)
    00320 {
    00321   char *new_cmd = strdup(name);
    00322   IConsoleCmd *item_new = MallocT<IConsoleCmd>(1);
    00323
    00324   item_new->next = NULL;
    00325   item_new->proc = proc;
    00326   item_new->name = new_cmd;
    00327
    00328   item_new->hook.access = NULL;
    00329   item_new->hook.pre = NULL;
    00330   item_new->hook.post = NULL;
    00331
    00332   IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command");
    00333 }
    00334
    00340 IConsoleCmd *IConsoleCmdGet(const char *name)
    00341 {
    00342   IConsoleCmd *item;
    00343
    00344   for (item = _iconsole_cmds; item != NULL; item = item->next) {
    00345     if (strcmp(item->name, name) == 0) return item;
    00346   }
    00347   return NULL;
    00348 }
    00349
    00355 void IConsoleAliasRegister(const char *name, const char *cmd)
    00356 {
    00357   char *new_alias = strdup(name);
    00358   char *cmd_aliased = strdup(cmd);
    00359   IConsoleAlias *item_new = MallocT<IConsoleAlias>(1);
    00360
    00361   item_new->next = NULL;
    00362   item_new->cmdline = cmd_aliased;
    00363   item_new->name = new_alias;
    00364
    00365   IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias");
    00366 }
    00367
    00373 IConsoleAlias *IConsoleAliasGet(const char *name)
    00374 {
    00375   IConsoleAlias *item;
    00376
    00377   for (item = _iconsole_aliases; item != NULL; item = item->next) {
    00378     if (strcmp(item->name, name) == 0) return item;
    00379   }
    00380
    00381   return NULL;
    00382 }
    00383
    00385 static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
    00386 {
    00387   /* len is the amount of bytes to add excluding the '\0'-termination */
    00388   int len = min(ICON_MAX_STREAMSIZE - bufpos - 1, (uint)strlen(src));
    00389   strecpy(dst, src, dst + len);
    00390
    00391   return len;
    00392 }
    00393
    00401 static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
    00402 {
    00403   const char *cmdptr;
    00404   char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
    00405   uint i;
    00406   uint a_index, astream_i;
    00407
    00408   memset(&aliases, 0, sizeof(aliases));
    00409   memset(&aliasstream, 0, sizeof(aliasstream));
    00410
    00411   if (_stdlib_con_developer)
    00412     IConsolePrintF(CC_DEBUG, "condbg: requested command is an alias; parsing...");
    00413
    00414   aliases[0] = aliasstream;
    00415   for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
    00416     if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
    00417
    00418     switch (*cmdptr) {
    00419     case '\'': // ' will double for ""
    00420       aliasstream[astream_i++] = '"';
    00421       break;
    00422     case ';': // Cmd seperator, start new command
    00423       aliasstream[astream_i] = '\0';
    00424       aliases[++a_index] = &aliasstream[++astream_i];
    00425       cmdptr++;
    00426       break;
    00427     case '%': // Some or all parameters
    00428       cmdptr++;
    00429       switch (*cmdptr) {
    00430       case '+': { // All parameters seperated: "[param 1]" "[param 2]"
    00431         for (i = 0; i != tokencount; i++) {
    00432           aliasstream[astream_i++] = '"';
    00433           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00434           aliasstream[astream_i++] = '"';
    00435           aliasstream[astream_i++] = ' ';
    00436         }
    00437       } break;
    00438       case '!': { // Merge the parameters to one: "[param 1] [param 2] [param 3...]"
    00439         aliasstream[astream_i++] = '"';
    00440         for (i = 0; i != tokencount; i++) {
    00441           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00442           aliasstream[astream_i++] = ' ';
    00443         }
    00444         aliasstream[astream_i++] = '"';
    00445
    00446       } break;
    00447         default: { // One specific parameter: %A = [param 1] %B = [param 2] ...
    00448         int param = *cmdptr - 'A';
    00449
    00450         if (param < 0 || param >= tokencount) {
    00451           IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
    00452           IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline);
    00453           return;
    00454         }
    00455
    00456         aliasstream[astream_i++] = '"';
    00457         astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
    00458         aliasstream[astream_i++] = '"';
    00459       } break;
    00460       } break;
    00461
    00462     default:
    00463       aliasstream[astream_i++] = *cmdptr;
    00464       break;
    00465     }
    00466   }
    00467
    00468   for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
    00469 }
    00470
    00480 void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help)
    00481 {
    00482   IConsoleVar *var;
    00483   IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help);
    00484   var = IConsoleVarGet(name);
    00485   var->size = size;
    00486 }
    00487
    00495 void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help)
    00496 {
    00497   char *new_cmd = strdup(name);
    00498   IConsoleVar *item_new = MallocT<IConsoleVar>(1);
    00499
    00500   item_new->help = (help != NULL) ? strdup(help) : NULL;
    00501
    00502   item_new->next = NULL;
    00503   item_new->name = new_cmd;
    00504   item_new->addr = addr;
    00505   item_new->proc = NULL;
    00506   item_new->type = type;
    00507
    00508   item_new->hook.access = NULL;
    00509   item_new->hook.pre = NULL;
    00510   item_new->hook.post = NULL;
    00511
    00512   IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable");
    00513 }
    00514
    00520 IConsoleVar *IConsoleVarGet(const char *name)
    00521 {
    00522   IConsoleVar *item;
    00523   for (item = _iconsole_vars; item != NULL; item = item->next) {
    00524     if (strcmp(item->name, name) == 0) return item;
    00525   }
    00526
    00527   return NULL;
    00528 }
    00529
    00535 static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
    00536 {
    00537   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00538   switch (var->type) {
    00539     case ICONSOLE_VAR_BOOLEAN:
    00540       *(bool*)var->addr = (value != 0);
    00541       break;
    00542     case ICONSOLE_VAR_BYTE:
    00543       *(byte*)var->addr = (byte)value;
    00544       break;
    00545     case ICONSOLE_VAR_UINT16:
    00546       *(uint16*)var->addr = (uint16)value;
    00547       break;
    00548     case ICONSOLE_VAR_INT16:
    00549       *(int16*)var->addr = (int16)value;
    00550       break;
    00551     case ICONSOLE_VAR_UINT32:
    00552       *(uint32*)var->addr = (uint32)value;
    00553       break;
    00554     case ICONSOLE_VAR_INT32:
    00555       *(int32*)var->addr = (int32)value;
    00556       break;
    00557     default: NOT_REACHED();
    00558   }
    00559
    00560   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00561   IConsoleVarPrintSetValue(var);
    00562 }
    00563
    00570 static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
    00571 {
    00572   if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
    00573
    00574   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00575   ttd_strlcpy((char*)var->addr, value, var->size);
    00576   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00577   IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
    00578   return;
    00579 }
    00580
    00586 static uint32 IConsoleVarGetValue(const IConsoleVar *var)
    00587 {
    00588   uint32 result = 0;
    00589
    00590   switch (var->type) {
    00591     case ICONSOLE_VAR_BOOLEAN:
    00592       result = *(bool*)var->addr;
    00593       break;
    00594     case ICONSOLE_VAR_BYTE:
    00595       result = *(byte*)var->addr;
    00596       break;
    00597     case ICONSOLE_VAR_UINT16:
    00598       result = *(uint16*)var->addr;
    00599       break;
    00600     case ICONSOLE_VAR_INT16:
    00601       result = *(int16*)var->addr;
    00602       break;
    00603     case ICONSOLE_VAR_UINT32:
    00604       result = *(uint32*)var->addr;
    00605       break;
    00606     case ICONSOLE_VAR_INT32:
    00607       result = *(int32*)var->addr;
    00608       break;
    00609     default: NOT_REACHED();
    00610   }
    00611   return result;
    00612 }
    00613
    00618 static char *IConsoleVarGetStringValue(const IConsoleVar *var)
    00619 {
    00620   static char tempres[50];
    00621   char *value = tempres;
    00622
    00623   switch (var->type) {
    00624     case ICONSOLE_VAR_BOOLEAN:
    00625       snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off");
    00626       break;
    00627     case ICONSOLE_VAR_BYTE:
    00628       snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr);
    00629       break;
    00630     case ICONSOLE_VAR_UINT16:
    00631       snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr);
    00632       break;
    00633     case ICONSOLE_VAR_UINT32:
    00634       snprintf(tempres, sizeof(tempres), "%u",  *(uint32*)var->addr);
    00635       break;
    00636     case ICONSOLE_VAR_INT16:
    00637       snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr);
    00638       break;
    00639     case ICONSOLE_VAR_INT32:
    00640       snprintf(tempres, sizeof(tempres), "%i",  *(int32*)var->addr);
    00641       break;
    00642     case ICONSOLE_VAR_STRING:
    00643       value = (char*)var->addr;
    00644       break;
    00645     default: NOT_REACHED();
    00646   }
    00647
    00648   return value;
    00649 }
    00650
    00654 void IConsoleVarPrintGetValue(const IConsoleVar *var)
    00655 {
    00656   char *value;
    00657   /* Some variables need really specific handling, handle this in its
    00658    * callback function */
    00659   if (var->proc != NULL) {
    00660     var->proc(0, NULL);
    00661     return;
    00662   }
    00663
    00664   value = IConsoleVarGetStringValue(var);
    00665   IConsolePrintF(CC_WARNING, "Current value for '%s' is:  %s", var->name, value);
    00666 }
    00667
    00672 void IConsoleVarPrintSetValue(const IConsoleVar *var)
    00673 {
    00674   char *value = IConsoleVarGetStringValue(var);
    00675   IConsolePrintF(CC_WARNING, "'%s' changed to:  %s", var->name, value);
    00676 }
    00677
    00685 void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT])
    00686 {
    00687   const char *tokenptr = token[0];
    00688   byte t_index = tokencount;
    00689   uint32 value;
    00690
    00691   if (_stdlib_con_developer)
    00692     IConsolePrintF(CC_DEBUG, "condbg: requested command is a variable");
    00693
    00694   if (tokencount == 0) { // Just print out value
    00695     IConsoleVarPrintGetValue(var);
    00696     return;
    00697   }
    00698
    00699   /* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */
    00700   if (strcmp(tokenptr, "=") == 0) tokencount--;
    00701
    00702   if (tokencount == 1) {
    00703     /* Some variables need really special handling, handle it in their callback procedure */
    00704     if (var->proc != NULL) {
    00705       var->proc(tokencount, &token[t_index - tokencount]); // set the new value
    00706       return;
    00707     }
    00708     /* Strings need special processing. No need to convert the argument to
    00709      * an integer value, just copy over the argument on a one-by-one basis */
    00710     if (var->type == ICONSOLE_VAR_STRING) {
    00711       IConsoleVarSetStringvalue(var, token[t_index - tokencount]);
    00712       return;
    00713     } else if (GetArgumentInteger(&value, token[t_index - tokencount])) {
    00714       IConsoleVarSetValue(var, value);
    00715       return;
    00716     }
    00717
    00718     /* Increase or decrease the value by one. This of course can only happen to 'number' types */
    00719     if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) {
    00720       IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1);
    00721       return;
    00722     }
    00723
    00724     if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) {
    00725       IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1);
    00726       return;
    00727     }
    00728   }
    00729
    00730   IConsoleError("invalid variable assignment");
    00731 }
    00732
    00739 void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc)
    00740 {
    00741   IConsoleVar *var = IConsoleVarGet(name);
    00742   if (var == NULL) return;
    00743   var->proc = proc;
    00744 }
    00745
    00751 void IConsoleCmdExec(const char *cmdstr)
    00752 {
    00753   IConsoleCmd   *cmd    = NULL;
    00754   IConsoleAlias *alias  = NULL;
    00755   IConsoleVar   *var    = NULL;
    00756
    00757   const char *cmdptr;
    00758   char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
    00759   uint t_index, tstream_i;
    00760
    00761   bool longtoken = false;
    00762   bool foundtoken = false;
    00763
    00764   if (cmdstr[0] == '#') return; // comments
    00765
    00766   for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
    00767     if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
    00768       IConsoleError("command contains malformed characters, aborting");
    00769       IConsolePrintF(CC_ERROR, "ERROR: command was: '%s'", cmdstr);
    00770       return;
    00771     }
    00772   }
    00773
    00774   if (_stdlib_con_developer)
    00775     IConsolePrintF(CC_DEBUG, "condbg: executing cmdline: '%s'", cmdstr);
    00776
    00777   memset(&tokens, 0, sizeof(tokens));
    00778   memset(&tokenstream, 0, sizeof(tokenstream));
    00779
    00780   /* 1. Split up commandline into tokens, seperated by spaces, commands
    00781    * enclosed in "" are taken as one token. We can only go as far as the amount
    00782    * of characters in our stream or the max amount of tokens we can handle */
    00783   for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
    00784     if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
    00785
    00786     switch (*cmdptr) {
    00787     case ' ': // Token seperator
    00788       if (!foundtoken) break;
    00789
    00790       if (longtoken) {
    00791         tokenstream[tstream_i] = *cmdptr;
    00792       } else {
    00793         tokenstream[tstream_i] = '\0';
    00794         foundtoken = false;
    00795       }
    00796
    00797       tstream_i++;
    00798       break;
    00799     case '"': // Tokens enclosed in "" are one token
    00800       longtoken = !longtoken;
    00801       break;
    00802     case '\\': // Escape character for ""
    00803       if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
    00804         tokenstream[tstream_i++] = *++cmdptr;
    00805         break;
    00806       }
    00807       /* fallthrough */
    00808     default: // Normal character
    00809       tokenstream[tstream_i++] = *cmdptr;
    00810
    00811       if (!foundtoken) {
    00812         tokens[t_index++] = &tokenstream[tstream_i - 1];
    00813         foundtoken = true;
    00814       }
    00815       break;
    00816     }
    00817   }
    00818
    00819   if (_stdlib_con_developer) {
    00820     uint i;
    00821
    00822     for (i = 0; tokens[i] != NULL; i++) {
    00823       IConsolePrintF(CC_DEBUG, "condbg: token %d is: '%s'", i, tokens[i]);
    00824     }
    00825   }
    00826
    00827   if (tokens[0] == '\0') return; // don't execute empty commands
    00828   /* 2. Determine type of command (cmd, alias or variable) and execute
    00829    * First try commands, then aliases, and finally variables. Execute
    00830    * the found action taking into account its hooking code
    00831    */
    00832   cmd = IConsoleCmdGet(tokens[0]);
    00833   if (cmd != NULL) {
    00834     if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
    00835       IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
    00836       if (cmd->proc(t_index, tokens)) { // index started with 0
    00837         IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
    00838       } else {
    00839         cmd->proc(0, NULL); // if command failed, give help
    00840       }
    00841     }
    00842     return;
    00843   }
    00844
    00845   t_index--; // ignore the variable-name for comfort for both aliases and variaables
    00846   alias = IConsoleAliasGet(tokens[0]);
    00847   if (alias != NULL) {
    00848     IConsoleAliasExec(alias, t_index, &tokens[1]);
    00849     return;
    00850   }
    00851
    00852   var = IConsoleVarGet(tokens[0]);
    00853   if (var != NULL) {
    00854     if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
    00855       IConsoleVarExec(var, t_index, &tokens[1]);
    00856     }
    00857     return;
    00858   }
    00859
    00860   IConsoleError("command or variable not found");
    00861 }