00001
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
00024 IConsoleCmd *_iconsole_cmds;
00025 IConsoleVar *_iconsole_vars;
00026 IConsoleAlias *_iconsole_aliases;
00027
00028
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
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
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
00091 NetworkServerSendRcon(_redirect_console_to_client, colour_code, string);
00092 return;
00093 }
00094 #endif
00095
00096
00097
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);
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
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 \
00281 if (_base == NULL) { \
00282 _base = item_new; \
00283 return; \
00284 } \
00285 \
00286 item_before = NULL; \
00287 item = _base; \
00288 \
00289 \
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; \
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 \
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
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 '\'':
00420 aliasstream[astream_i++] = '"';
00421 break;
00422 case ';':
00423 aliasstream[astream_i] = '\0';
00424 aliases[++a_index] = &aliasstream[++astream_i];
00425 cmdptr++;
00426 break;
00427 case '%':
00428 cmdptr++;
00429 switch (*cmdptr) {
00430 case '+': {
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 '!': {
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: {
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]);
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);
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
00658
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) {
00695 IConsoleVarPrintGetValue(var);
00696 return;
00697 }
00698
00699
00700 if (strcmp(tokenptr, "=") == 0) tokencount--;
00701
00702 if (tokencount == 1) {
00703
00704 if (var->proc != NULL) {
00705 var->proc(tokencount, &token[t_index - tokencount]);
00706 return;
00707 }
00708
00709
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
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;
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
00781
00782
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 ' ':
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 '"':
00800 longtoken = !longtoken;
00801 break;
00802 case '\\':
00803 if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
00804 tokenstream[tstream_i++] = *++cmdptr;
00805 break;
00806 }
00807
00808 default:
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;
00828
00829
00830
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)) {
00837 IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
00838 } else {
00839 cmd->proc(0, NULL);
00840 }
00841 }
00842 return;
00843 }
00844
00845 t_index--;
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 }