# console.cpp

Go to the documentation of this file.

    00001 /* $Id: console.cpp 18809 2010-01-15 16:41:15Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #include "stdafx.h"
    00013 #include "console_internal.h"
    00014 #include "network/network.h"
    00015 #include "network/network_func.h"
    00016 #include "debug.h"
    00017 #include "console_func.h"
    00018
    00019 #include <stdarg.h>
    00020
    00021 #define ICON_BUFFER 79
    00022 #define ICON_HISTORY_SIZE 20
    00023 #define ICON_LINE_HEIGHT 12
    00024 #define ICON_RIGHT_BORDERWIDTH 10
    00025 #define ICON_BOTTOM_BORDERWIDTH 12
    00026 #define ICON_MAX_ALIAS_LINES 40
    00027 #define ICON_TOKEN_COUNT 20
    00028
    00029 /* console parser */
    00030 IConsoleCmd   *_iconsole_cmds;
    00031 IConsoleVar   *_iconsole_vars;
    00032 IConsoleAlias *_iconsole_aliases;
    00033
    00034 /* ** stdlib ** */
    00035 byte _stdlib_developer = 1;
    00036 bool _stdlib_con_developer = false;
    00037 FILE *_iconsole_output_file;
    00038
    00039 void IConsoleInit()
    00040 {
    00041   _iconsole_output_file = NULL;
    00042 #ifdef ENABLE_NETWORK /* Initialize network only variables */
    00043   _redirect_console_to_client = INVALID_CLIENT_ID;
    00044 #endif
    00045
    00046   IConsoleGUIInit();
    00047
    00048   IConsoleStdLibRegister();
    00049 }
    00050
    00051 static void IConsoleWriteToLogFile(const char *string)
    00052 {
    00053   if (_iconsole_output_file != NULL) {
    00054     /* if there is an console output file ... also print it there */
    00055     const char *header = GetLogPrefix();
    00056     if (fwrite(header, strlen(header), 1, _iconsole_output_file) != 1 ||
    00057         fwrite(string, strlen(string), 1, _iconsole_output_file) != 1 ||
    00058         fwrite("\n", 1, 1, _iconsole_output_file) != 1) {
    00059       fclose(_iconsole_output_file);
    00060       _iconsole_output_file = NULL;
    00061       IConsolePrintF(CC_DEFAULT, "cannot write to log file");
    00062     }
    00063   }
    00064 }
    00065
    00066 bool CloseConsoleLogIfActive()
    00067 {
    00068   if (_iconsole_output_file != NULL) {
    00069     IConsolePrintF(CC_DEFAULT, "file output complete");
    00070     fclose(_iconsole_output_file);
    00071     _iconsole_output_file = NULL;
    00072     return true;
    00073   }
    00074
    00075   return false;
    00076 }
    00077
    00078 void IConsoleFree()
    00079 {
    00080   IConsoleGUIFree();
    00081   CloseConsoleLogIfActive();
    00082 }
    00083
    00093 void IConsolePrint(ConsoleColour colour_code, const char *string)
    00094 {
    00095   char *str;
    00096 #ifdef ENABLE_NETWORK
    00097   if (_redirect_console_to_client != INVALID_CLIENT_ID) {
    00098     /* Redirect the string to the client */
    00099     NetworkServerSendRcon(_redirect_console_to_client, colour_code, string);
    00100     return;
    00101   }
    00102 #endif
    00103
    00104   /* Create a copy of the string, strip if of colours and invalid
    00105    * characters and (when applicable) assign it to the console buffer */
    00106   str = strdup(string);
    00107   str_strip_colours(str);
    00108   str_validate(str, str + strlen(str));
    00109
    00110   if (_network_dedicated) {
    00111     fprintf(stdout, "%s%s\n", GetLogPrefix(), str);
    00112     fflush(stdout);
    00113     IConsoleWriteToLogFile(str);
    00114     free(str); // free duplicated string since it's not used anymore
    00115     return;
    00116   }
    00117
    00118   IConsoleWriteToLogFile(str);
    00119   IConsoleGUIPrint(colour_code, str);
    00120 }
    00121
    00127 void CDECL IConsolePrintF(ConsoleColour colour_code, const char *format, ...)
    00128 {
    00129   va_list va;
    00130   char buf[ICON_MAX_STREAMSIZE];
    00131
    00132   va_start(va, format);
    00133   vsnprintf(buf, sizeof(buf), format, va);
    00134   va_end(va);
    00135
    00136   IConsolePrint(colour_code, buf);
    00137 }
    00138
    00147 void IConsoleDebug(const char *dbg, const char *string)
    00148 {
    00149   if (_stdlib_developer > 1)
    00150     IConsolePrintF(CC_DEBUG, "dbg: [%s] %s", dbg, string);
    00151 }
    00152
    00158 void IConsoleWarning(const char *string)
    00159 {
    00160   if (_stdlib_developer > 0)
    00161     IConsolePrintF(CC_WARNING, "WARNING: %s", string);
    00162 }
    00163
    00168 void IConsoleError(const char *string)
    00169 {
    00170   IConsolePrintF(CC_ERROR, "ERROR: %s", string);
    00171 }
    00172
    00180 bool GetArgumentInteger(uint32 *value, const char *arg)
    00181 {
    00182   char *endptr;
    00183
    00184   if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
    00185     *value = 1;
    00186     return true;
    00187   }
    00188   if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
    00189     *value = 0;
    00190     return true;
    00191   }
    00192
    00193   *value = strtoul(arg, &endptr, 0);
    00194   return arg != endptr;
    00195 }
    00196
    00197 /*  * *************************
    00198     * hooking code            *
    00199     * *************************/
    00200
    00207 static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc)
    00208 {
    00209   if (hooks == NULL || proc == NULL) return;
    00210
    00211   switch (type) {
    00212     case ICONSOLE_HOOK_ACCESS:
    00213       hooks->access = proc;
    00214       break;
    00215     case ICONSOLE_HOOK_PRE_ACTION:
    00216       hooks->pre = proc;
    00217       break;
    00218     case ICONSOLE_HOOK_POST_ACTION:
    00219       hooks->post = proc;
    00220       break;
    00221     default: NOT_REACHED();
    00222   }
    00223 }
    00224
    00233 static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type)
    00234 {
    00235   IConsoleHook *proc = NULL;
    00236   if (hooks == NULL) return false;
    00237
    00238   switch (type) {
    00239     case ICONSOLE_HOOK_ACCESS:
    00240       proc = hooks->access;
    00241       break;
    00242     case ICONSOLE_HOOK_PRE_ACTION:
    00243       proc = hooks->pre;
    00244       break;
    00245     case ICONSOLE_HOOK_POST_ACTION:
    00246       proc = hooks->post;
    00247       break;
    00248     default: NOT_REACHED();
    00249   }
    00250
    00251   return (proc == NULL) ? true : proc();
    00252 }
    00253
    00260 void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00261 {
    00262   IConsoleCmd *cmd = IConsoleCmdGet(name);
    00263   if (cmd == NULL) return;
    00264   IConsoleHookAdd(&cmd->hook, type, proc);
    00265 }
    00266
    00273 void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00274 {
    00275   IConsoleVar *var = IConsoleVarGet(name);
    00276   if (var == NULL) return;
    00277   IConsoleHookAdd(&var->hook, type, proc);
    00278 }
    00279
    00285 #define IConsoleAddSorted(_base, item_new, IConsoleType, type)                 \
    00286 {                                                                              \
    00287   IConsoleType *item, *item_before;                                            \
    00288   /* first command */                                                          \
    00289   if (_base == NULL) {                                                         \
    00290     _base = item_new;                                                          \
    00291     return;                                                                    \
    00292   }                                                                            \
    00293                                                                                \
    00294   item_before = NULL;                                                          \
    00295   item = _base;                                                                \
    00296                                                                                \
    00297   /* BEGIN - Alphabetically insert the commands into the linked list */        \
    00298   while (item != NULL) {                                                       \
    00299     int i = strcmp(item->name, item_new->name);                                \
    00300     if (i == 0) {                                                              \
    00301       IConsoleError(type " with this name already exists; insertion aborted"); \
    00302       free(item_new);                                                          \
    00303       return;                                                                  \
    00304     }                                                                          \
    00305                                                                                \
    00306     if (i > 0) break; /* insert at this position */                            \
    00307                                                                                \
    00308     item_before = item;                                                        \
    00309     item = item->next;                                                         \
    00310   }                                                                            \
    00311                                                                                \
    00312   if (item_before == NULL) {                                                   \
    00313     _base = item_new;                                                          \
    00314   } else {                                                                     \
    00315     item_before->next = item_new;                                              \
    00316   }                                                                            \
    00317                                                                                \
    00318   item_new->next = item;                                                       \
    00319   /* END - Alphabetical insert */                                              \
    00320 }
    00321
    00327 void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc)
    00328 {
    00329   char *new_cmd = strdup(name);
    00330   IConsoleCmd *item_new = MallocT<IConsoleCmd>(1);
    00331
    00332   item_new->next = NULL;
    00333   item_new->proc = proc;
    00334   item_new->name = new_cmd;
    00335
    00336   item_new->hook.access = NULL;
    00337   item_new->hook.pre = NULL;
    00338   item_new->hook.post = NULL;
    00339
    00340   IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command");
    00341 }
    00342
    00348 IConsoleCmd *IConsoleCmdGet(const char *name)
    00349 {
    00350   IConsoleCmd *item;
    00351
    00352   for (item = _iconsole_cmds; item != NULL; item = item->next) {
    00353     if (strcmp(item->name, name) == 0) return item;
    00354   }
    00355   return NULL;
    00356 }
    00357
    00363 void IConsoleAliasRegister(const char *name, const char *cmd)
    00364 {
    00365   char *new_alias = strdup(name);
    00366   char *cmd_aliased = strdup(cmd);
    00367   IConsoleAlias *item_new = MallocT<IConsoleAlias>(1);
    00368
    00369   item_new->next = NULL;
    00370   item_new->cmdline = cmd_aliased;
    00371   item_new->name = new_alias;
    00372
    00373   IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias");
    00374 }
    00375
    00381 IConsoleAlias *IConsoleAliasGet(const char *name)
    00382 {
    00383   IConsoleAlias *item;
    00384
    00385   for (item = _iconsole_aliases; item != NULL; item = item->next) {
    00386     if (strcmp(item->name, name) == 0) return item;
    00387   }
    00388
    00389   return NULL;
    00390 }
    00391
    00393 static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
    00394 {
    00395   /* len is the amount of bytes to add excluding the '\0'-termination */
    00396   int len = min(ICON_MAX_STREAMSIZE - bufpos - 1, (uint)strlen(src));
    00397   strecpy(dst, src, dst + len);
    00398
    00399   return len;
    00400 }
    00401
    00409 static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
    00410 {
    00411   const char *cmdptr;
    00412   char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
    00413   uint i;
    00414   uint a_index, astream_i;
    00415
    00416   memset(&aliases, 0, sizeof(aliases));
    00417   memset(&aliasstream, 0, sizeof(aliasstream));
    00418
    00419   if (_stdlib_con_developer)
    00420     IConsolePrintF(CC_DEBUG, "condbg: requested command is an alias; parsing...");
    00421
    00422   aliases[0] = aliasstream;
    00423   for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
    00424     if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
    00425
    00426     switch (*cmdptr) {
    00427     case '\'': // ' will double for ""
    00428       aliasstream[astream_i++] = '"';
    00429       break;
    00430     case ';': // Cmd seperator, start new command
    00431       aliasstream[astream_i] = '\0';
    00432       aliases[++a_index] = &aliasstream[++astream_i];
    00433       cmdptr++;
    00434       break;
    00435     case '%': // Some or all parameters
    00436       cmdptr++;
    00437       switch (*cmdptr) {
    00438       case '+': { // All parameters seperated: "[param 1]" "[param 2]"
    00439         for (i = 0; i != tokencount; i++) {
    00440           aliasstream[astream_i++] = '"';
    00441           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00442           aliasstream[astream_i++] = '"';
    00443           aliasstream[astream_i++] = ' ';
    00444         }
    00445       } break;
    00446       case '!': { // Merge the parameters to one: "[param 1] [param 2] [param 3...]"
    00447         aliasstream[astream_i++] = '"';
    00448         for (i = 0; i != tokencount; i++) {
    00449           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00450           aliasstream[astream_i++] = ' ';
    00451         }
    00452         aliasstream[astream_i++] = '"';
    00453
    00454       } break;
    00455         default: { // One specific parameter: %A = [param 1] %B = [param 2] ...
    00456         int param = *cmdptr - 'A';
    00457
    00458         if (param < 0 || param >= tokencount) {
    00459           IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
    00460           IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline);
    00461           return;
    00462         }
    00463
    00464         aliasstream[astream_i++] = '"';
    00465         astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
    00466         aliasstream[astream_i++] = '"';
    00467       } break;
    00468       } break;
    00469
    00470     default:
    00471       aliasstream[astream_i++] = *cmdptr;
    00472       break;
    00473     }
    00474   }
    00475
    00476   for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
    00477 }
    00478
    00488 void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help)
    00489 {
    00490   IConsoleVar *var;
    00491   IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help);
    00492   var = IConsoleVarGet(name);
    00493   var->size = size;
    00494 }
    00495
    00503 void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help)
    00504 {
    00505   char *new_cmd = strdup(name);
    00506   IConsoleVar *item_new = MallocT<IConsoleVar>(1);
    00507
    00508   item_new->help = (help != NULL) ? strdup(help) : NULL;
    00509
    00510   item_new->next = NULL;
    00511   item_new->name = new_cmd;
    00512   item_new->addr = addr;
    00513   item_new->proc = NULL;
    00514   item_new->type = type;
    00515
    00516   item_new->hook.access = NULL;
    00517   item_new->hook.pre = NULL;
    00518   item_new->hook.post = NULL;
    00519
    00520   IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable");
    00521 }
    00522
    00528 IConsoleVar *IConsoleVarGet(const char *name)
    00529 {
    00530   IConsoleVar *item;
    00531   for (item = _iconsole_vars; item != NULL; item = item->next) {
    00532     if (strcmp(item->name, name) == 0) return item;
    00533   }
    00534
    00535   return NULL;
    00536 }
    00537
    00542 static char *IConsoleVarGetStringValue(const IConsoleVar *var)
    00543 {
    00544   static char tempres[50];
    00545   char *value = tempres;
    00546
    00547   switch (var->type) {
    00548     case ICONSOLE_VAR_BOOLEAN:
    00549       snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off");
    00550       break;
    00551     case ICONSOLE_VAR_BYTE:
    00552       snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr);
    00553       break;
    00554     case ICONSOLE_VAR_UINT16:
    00555       snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr);
    00556       break;
    00557     case ICONSOLE_VAR_UINT32:
    00558       snprintf(tempres, sizeof(tempres), "%u",  *(uint32*)var->addr);
    00559       break;
    00560     case ICONSOLE_VAR_INT16:
    00561       snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr);
    00562       break;
    00563     case ICONSOLE_VAR_INT32:
    00564       snprintf(tempres, sizeof(tempres), "%i",  *(int32*)var->addr);
    00565       break;
    00566     case ICONSOLE_VAR_STRING:
    00567       value = (char*)var->addr;
    00568       break;
    00569     default: NOT_REACHED();
    00570   }
    00571
    00572   return value;
    00573 }
    00574
    00579 static void IConsoleVarPrintSetValue(const IConsoleVar *var)
    00580 {
    00581   char *value = IConsoleVarGetStringValue(var);
    00582   IConsolePrintF(CC_WARNING, "'%s' changed to:  %s", var->name, value);
    00583 }
    00584
    00590 static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
    00591 {
    00592   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00593   switch (var->type) {
    00594     case ICONSOLE_VAR_BOOLEAN:
    00595       *(bool*)var->addr = (value != 0);
    00596       break;
    00597     case ICONSOLE_VAR_BYTE:
    00598       *(byte*)var->addr = (byte)value;
    00599       break;
    00600     case ICONSOLE_VAR_UINT16:
    00601       *(uint16*)var->addr = (uint16)value;
    00602       break;
    00603     case ICONSOLE_VAR_INT16:
    00604       *(int16*)var->addr = (int16)value;
    00605       break;
    00606     case ICONSOLE_VAR_UINT32:
    00607       *(uint32*)var->addr = (uint32)value;
    00608       break;
    00609     case ICONSOLE_VAR_INT32:
    00610       *(int32*)var->addr = (int32)value;
    00611       break;
    00612     default: NOT_REACHED();
    00613   }
    00614
    00615   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00616   IConsoleVarPrintSetValue(var);
    00617 }
    00618
    00625 static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
    00626 {
    00627   if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
    00628
    00629   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00630   ttd_strlcpy((char*)var->addr, value, var->size);
    00631   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00632   IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
    00633   return;
    00634 }
    00635
    00641 static uint32 IConsoleVarGetValue(const IConsoleVar *var)
    00642 {
    00643   uint32 result = 0;
    00644
    00645   switch (var->type) {
    00646     case ICONSOLE_VAR_BOOLEAN:
    00647       result = *(bool*)var->addr;
    00648       break;
    00649     case ICONSOLE_VAR_BYTE:
    00650       result = *(byte*)var->addr;
    00651       break;
    00652     case ICONSOLE_VAR_UINT16:
    00653       result = *(uint16*)var->addr;
    00654       break;
    00655     case ICONSOLE_VAR_INT16:
    00656       result = *(int16*)var->addr;
    00657       break;
    00658     case ICONSOLE_VAR_UINT32:
    00659       result = *(uint32*)var->addr;
    00660       break;
    00661     case ICONSOLE_VAR_INT32:
    00662       result = *(int32*)var->addr;
    00663       break;
    00664     default: NOT_REACHED();
    00665   }
    00666   return result;
    00667 }
    00668
    00672 void IConsoleVarPrintGetValue(const IConsoleVar *var)
    00673 {
    00674   char *value;
    00675   /* Some variables need really specific handling, handle this in its
    00676    * callback function */
    00677   if (var->proc != NULL) {
    00678     var->proc(0, NULL);
    00679     return;
    00680   }
    00681
    00682   value = IConsoleVarGetStringValue(var);
    00683   IConsolePrintF(CC_WARNING, "Current value for '%s' is:  %s", var->name, value);
    00684 }
    00685
    00693 static void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT])
    00694 {
    00695   const char *tokenptr = token[0];
    00696   byte t_index = tokencount;
    00697   uint32 value;
    00698
    00699   if (_stdlib_con_developer)
    00700     IConsolePrintF(CC_DEBUG, "condbg: requested command is a variable");
    00701
    00702   if (tokencount == 0) { // Just print out value
    00703     IConsoleVarPrintGetValue(var);
    00704     return;
    00705   }
    00706
    00707   /* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */
    00708   if (strcmp(tokenptr, "=") == 0) tokencount--;
    00709
    00710   if (tokencount == 1) {
    00711     /* Some variables need really special handling, handle it in their callback procedure */
    00712     if (var->proc != NULL) {
    00713       var->proc(tokencount, &token[t_index - tokencount]); // set the new value
    00714       return;
    00715     }
    00716     /* Strings need special processing. No need to convert the argument to
    00717      * an integer value, just copy over the argument on a one-by-one basis */
    00718     if (var->type == ICONSOLE_VAR_STRING) {
    00719       IConsoleVarSetStringvalue(var, token[t_index - tokencount]);
    00720       return;
    00721     } else if (GetArgumentInteger(&value, token[t_index - tokencount])) {
    00722       IConsoleVarSetValue(var, value);
    00723       return;
    00724     }
    00725
    00726     /* Increase or decrease the value by one. This of course can only happen to 'number' types */
    00727     if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) {
    00728       IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1);
    00729       return;
    00730     }
    00731
    00732     if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) {
    00733       IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1);
    00734       return;
    00735     }
    00736   }
    00737
    00738   IConsoleError("invalid variable assignment");
    00739 }
    00740
    00747 void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc)
    00748 {
    00749   IConsoleVar *var = IConsoleVarGet(name);
    00750   if (var == NULL) return;
    00751   var->proc = proc;
    00752 }
    00753
    00759 void IConsoleCmdExec(const char *cmdstr)
    00760 {
    00761   IConsoleCmd   *cmd    = NULL;
    00762   IConsoleAlias *alias  = NULL;
    00763   IConsoleVar   *var    = NULL;
    00764
    00765   const char *cmdptr;
    00766   char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
    00767   uint t_index, tstream_i;
    00768
    00769   bool longtoken = false;
    00770   bool foundtoken = false;
    00771
    00772   if (cmdstr[0] == '#') return; // comments
    00773
    00774   for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
    00775     if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
    00776       IConsoleError("command contains malformed characters, aborting");
    00777       IConsolePrintF(CC_ERROR, "ERROR: command was: '%s'", cmdstr);
    00778       return;
    00779     }
    00780   }
    00781
    00782   if (_stdlib_con_developer)
    00783     IConsolePrintF(CC_DEBUG, "condbg: executing cmdline: '%s'", cmdstr);
    00784
    00785   memset(&tokens, 0, sizeof(tokens));
    00786   memset(&tokenstream, 0, sizeof(tokenstream));
    00787
    00788   /* 1. Split up commandline into tokens, seperated by spaces, commands
    00789    * enclosed in "" are taken as one token. We can only go as far as the amount
    00790    * of characters in our stream or the max amount of tokens we can handle */
    00791   for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
    00792     if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
    00793
    00794     switch (*cmdptr) {
    00795     case ' ': // Token seperator
    00796       if (!foundtoken) break;
    00797
    00798       if (longtoken) {
    00799         tokenstream[tstream_i] = *cmdptr;
    00800       } else {
    00801         tokenstream[tstream_i] = '\0';
    00802         foundtoken = false;
    00803       }
    00804
    00805       tstream_i++;
    00806       break;
    00807     case '"': // Tokens enclosed in "" are one token
    00808       longtoken = !longtoken;
    00809       break;
    00810     case '\\': // Escape character for ""
    00811       if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
    00812         tokenstream[tstream_i++] = *++cmdptr;
    00813         break;
    00814       }
    00815       /* fallthrough */
    00816     default: // Normal character
    00817       tokenstream[tstream_i++] = *cmdptr;
    00818
    00819       if (!foundtoken) {
    00820         tokens[t_index++] = &tokenstream[tstream_i - 1];
    00821         foundtoken = true;
    00822       }
    00823       break;
    00824     }
    00825   }
    00826
    00827   if (_stdlib_con_developer) {
    00828     uint i;
    00829
    00830     for (i = 0; tokens[i] != NULL; i++) {
    00831       IConsolePrintF(CC_DEBUG, "condbg: token %d is: '%s'", i, tokens[i]);
    00832     }
    00833   }
    00834
    00835   if (tokens[0] == '\0') return; // don't execute empty commands
    00836   /* 2. Determine type of command (cmd, alias or variable) and execute
    00837    * First try commands, then aliases, and finally variables. Execute
    00838    * the found action taking into account its hooking code
    00839    */
    00840   cmd = IConsoleCmdGet(tokens[0]);
    00841   if (cmd != NULL) {
    00842     if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
    00843       IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
    00844       if (cmd->proc(t_index, tokens)) { // index started with 0
    00845         IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
    00846       } else {
    00847         cmd->proc(0, NULL); // if command failed, give help
    00848       }
    00849     }
    00850     return;
    00851   }
    00852
    00853   t_index--; // ignore the variable-name for comfort for both aliases and variaables
    00854   alias = IConsoleAliasGet(tokens[0]);
    00855   if (alias != NULL) {
    00856     IConsoleAliasExec(alias, t_index, &tokens[1]);
    00857     return;
    00858   }
    00859
    00860   var = IConsoleVarGet(tokens[0]);
    00861   if (var != NULL) {
    00862     if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
    00863       IConsoleVarExec(var, t_index, &tokens[1]);
    00864     }
    00865     return;
    00866   }
    00867
    00868   IConsoleError("command or variable not found");
    00869 }