# console.cpp

Go to the documentation of this file.

    00001 /* $Id: console.cpp 14426 2008-10-01 11:48:57Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "gui.h"
    00008 #include "textbuf_gui.h"
    00009 #include "window_gui.h"
    00010 #include "variables.h"
    00011 #include <stdarg.h>
    00012 #include <string.h>
    00013 #include "console.h"
    00014 #include "network/network.h"
    00015 #include "network/network_data.h"
    00016 #include "network/network_server.h"
    00017 #include "core/alloc_func.hpp"
    00018 #include "window_func.h"
    00019 #include "string_func.h"
    00020 #include "gfx_func.h"
    00021
    00022 #include "table/strings.h"
    00023
    00024 #define ICON_BUFFER 79
    00025 #define ICON_HISTORY_SIZE 20
    00026 #define ICON_LINE_HEIGHT 12
    00027 #define ICON_RIGHT_BORDERWIDTH 10
    00028 #define ICON_BOTTOM_BORDERWIDTH 12
    00029 #define ICON_MAX_ALIAS_LINES 40
    00030 #define ICON_TOKEN_COUNT 20
    00031
    00032 /* console parser */
    00033 IConsoleCmd   *_iconsole_cmds;
    00034 IConsoleVar   *_iconsole_vars;
    00035 IConsoleAlias *_iconsole_aliases;
    00036
    00037 /* console colors/modes */
    00038 byte _icolour_def;
    00039 byte _icolour_err;
    00040 byte _icolour_warn;
    00041 byte _icolour_dbg;
    00042 byte _icolour_cmd;
    00043 IConsoleModes _iconsole_mode;
    00044
    00045 /* ** main console ** */
    00046 static char *_iconsole_buffer[ICON_BUFFER + 1];
    00047 static uint16 _iconsole_cbuffer[ICON_BUFFER + 1];
    00048 static Textbuf _iconsole_cmdline;
    00049
    00050 /* ** stdlib ** */
    00051 byte _stdlib_developer = 1;
    00052 bool _stdlib_con_developer = false;
    00053 FILE *_iconsole_output_file;
    00054
    00055 /* ** main console cmd buffer ** */
    00056 static char *_iconsole_history[ICON_HISTORY_SIZE];
    00057 static byte _iconsole_historypos;
    00058
    00059 /* *************** *
    00060  *  end of header  *
    00061  * *************** */
    00062
    00063 static void IConsoleClearCommand()
    00064 {
    00065   memset(_iconsole_cmdline.buf, 0, ICON_CMDLN_SIZE);
    00066   _iconsole_cmdline.length = 0;
    00067   _iconsole_cmdline.width = 0;
    00068   _iconsole_cmdline.caretpos = 0;
    00069   _iconsole_cmdline.caretxoffs = 0;
    00070   SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
    00071 }
    00072
    00073 static inline void IConsoleResetHistoryPos() {_iconsole_historypos = ICON_HISTORY_SIZE - 1;}
    00074
    00075
    00076 static void IConsoleHistoryAdd(const char *cmd);
    00077 static void IConsoleHistoryNavigate(int direction);
    00078
    00079 /* ** console window ** */
    00080 static void IConsoleWndProc(Window *w, WindowEvent *e)
    00081 {
    00082   static byte iconsole_scroll = ICON_BUFFER;
    00083
    00084   switch (e->event) {
    00085     case WE_PAINT: {
    00086       int i = iconsole_scroll;
    00087       int max = (w->height / ICON_LINE_HEIGHT) - 1;
    00088       int delta = 0;
    00089       GfxFillRect(w->left, w->top, w->width, w->height - 1, 0);
    00090       while ((i > 0) && (i > iconsole_scroll - max) && (_iconsole_buffer[i] != NULL)) {
    00091         DoDrawString(_iconsole_buffer[i], 5,
    00092           w->height - (iconsole_scroll + 2 - i) * ICON_LINE_HEIGHT, _iconsole_cbuffer[i]);
    00093         i--;
    00094       }
    00095       /* If the text is longer than the window, don't show the starting ']' */
    00096       delta = w->width - 10 - _iconsole_cmdline.width - ICON_RIGHT_BORDERWIDTH;
    00097       if (delta > 0) {
    00098         DoDrawString("]", 5, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
    00099         delta = 0;
    00100       }
    00101
    00102       DoDrawString(_iconsole_cmdline.buf, 10 + delta, w->height - ICON_LINE_HEIGHT, _icolour_cmd);
    00103
    00104       if (_iconsole_cmdline.caret)
    00105         DoDrawString("_", 10 + delta + _iconsole_cmdline.caretxoffs, w->height - ICON_LINE_HEIGHT, TC_WHITE);
    00106       break;
    00107     }
    00108     case WE_MOUSELOOP:
    00109       if (HandleCaret(&_iconsole_cmdline))
    00110         SetWindowDirty(w);
    00111       break;
    00112     case WE_DESTROY:
    00113       _iconsole_mode = ICONSOLE_CLOSED;
    00114       break;
    00115     case WE_KEYPRESS:
    00116       e->we.keypress.cont = false;
    00117       switch (e->we.keypress.keycode) {
    00118         case WKC_UP:
    00119           IConsoleHistoryNavigate(+1);
    00120           SetWindowDirty(w);
    00121           break;
    00122         case WKC_DOWN:
    00123           IConsoleHistoryNavigate(-1);
    00124           SetWindowDirty(w);
    00125           break;
    00126         case WKC_SHIFT | WKC_PAGEUP:
    00127           if (iconsole_scroll - (w->height / ICON_LINE_HEIGHT) - 1 < 0) {
    00128             iconsole_scroll = 0;
    00129           } else {
    00130             iconsole_scroll -= (w->height / ICON_LINE_HEIGHT) - 1;
    00131           }
    00132           SetWindowDirty(w);
    00133           break;
    00134         case WKC_SHIFT | WKC_PAGEDOWN:
    00135           if (iconsole_scroll + (w->height / ICON_LINE_HEIGHT) - 1 > ICON_BUFFER) {
    00136             iconsole_scroll = ICON_BUFFER;
    00137           } else {
    00138             iconsole_scroll += (w->height / ICON_LINE_HEIGHT) - 1;
    00139           }
    00140           SetWindowDirty(w);
    00141           break;
    00142         case WKC_SHIFT | WKC_UP:
    00143           if (iconsole_scroll <= 0) {
    00144             iconsole_scroll = 0;
    00145           } else {
    00146             --iconsole_scroll;
    00147           }
    00148           SetWindowDirty(w);
    00149           break;
    00150         case WKC_SHIFT | WKC_DOWN:
    00151           if (iconsole_scroll >= ICON_BUFFER) {
    00152             iconsole_scroll = ICON_BUFFER;
    00153           } else {
    00154             ++iconsole_scroll;
    00155           }
    00156           SetWindowDirty(w);
    00157           break;
    00158         case WKC_BACKQUOTE:
    00159           IConsoleSwitch();
    00160           break;
    00161         case WKC_RETURN: case WKC_NUM_ENTER:
    00162           IConsolePrintF(_icolour_cmd, "] %s", _iconsole_cmdline.buf);
    00163           IConsoleHistoryAdd(_iconsole_cmdline.buf);
    00164
    00165           IConsoleCmdExec(_iconsole_cmdline.buf);
    00166           IConsoleClearCommand();
    00167           break;
    00168         case WKC_CTRL | WKC_RETURN:
    00169           _iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
    00170           IConsoleResize(w);
    00171           MarkWholeScreenDirty();
    00172           break;
    00173         case (WKC_CTRL | 'V'):
    00174           if (InsertTextBufferClipboard(&_iconsole_cmdline)) {
    00175             IConsoleResetHistoryPos();
    00176             SetWindowDirty(w);
    00177           }
    00178           break;
    00179         case (WKC_CTRL | 'L'):
    00180           IConsoleCmdExec("clear");
    00181           break;
    00182         case (WKC_CTRL | 'U'):
    00183           DeleteTextBufferAll(&_iconsole_cmdline);
    00184           SetWindowDirty(w);
    00185           break;
    00186         case WKC_BACKSPACE: case WKC_DELETE:
    00187           if (DeleteTextBufferChar(&_iconsole_cmdline, e->we.keypress.keycode)) {
    00188             IConsoleResetHistoryPos();
    00189             SetWindowDirty(w);
    00190           }
    00191           break;
    00192         case WKC_LEFT: case WKC_RIGHT: case WKC_END: case WKC_HOME:
    00193           if (MoveTextBufferPos(&_iconsole_cmdline, e->we.keypress.keycode)) {
    00194             IConsoleResetHistoryPos();
    00195             SetWindowDirty(w);
    00196           }
    00197           break;
    00198         default:
    00199           if (IsValidChar(e->we.keypress.key, CS_ALPHANUMERAL)) {
    00200             iconsole_scroll = ICON_BUFFER;
    00201             InsertTextBufferChar(&_iconsole_cmdline, e->we.keypress.key);
    00202             IConsoleResetHistoryPos();
    00203             SetWindowDirty(w);
    00204           } else {
    00205             e->we.keypress.cont = true;
    00206           }
    00207       break;
    00208     }
    00209   }
    00210 }
    00211
    00212 static const Widget _iconsole_window_widgets[] = {
    00213   {WIDGETS_END}
    00214 };
    00215
    00216 static const WindowDesc _iconsole_window_desc = {
    00217   0, 0, 2, 2, 2, 2,
    00218   WC_CONSOLE, WC_NONE,
    00219   WDF_STD_TOOLTIPS | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
    00220   _iconsole_window_widgets,
    00221   IConsoleWndProc,
    00222 };
    00223
    00224 void IConsoleInit()
    00225 {
    00226   extern const char _openttd_revision[];
    00227   _iconsole_output_file = NULL;
    00228   _icolour_def  =  1;
    00229   _icolour_err  =  3;
    00230   _icolour_warn = 13;
    00231   _icolour_dbg  =  5;
    00232   _icolour_cmd  = TC_GOLD;
    00233   _iconsole_historypos = ICON_HISTORY_SIZE - 1;
    00234   _iconsole_mode = ICONSOLE_CLOSED;
    00235
    00236 #ifdef ENABLE_NETWORK /* Initialize network only variables */
    00237   _redirect_console_to_client = 0;
    00238 #endif
    00239
    00240   memset(_iconsole_history, 0, sizeof(_iconsole_history));
    00241   memset(_iconsole_buffer, 0, sizeof(_iconsole_buffer));
    00242   memset(_iconsole_cbuffer, 0, sizeof(_iconsole_cbuffer));
    00243   _iconsole_cmdline.buf = CallocT<char>(ICON_CMDLN_SIZE); // create buffer and zero it
    00244   _iconsole_cmdline.maxlength = ICON_CMDLN_SIZE;
    00245
    00246   IConsolePrintF(13, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
    00247   IConsolePrint(12,  "------------------------------------");
    00248   IConsolePrint(12,  "use \"help\" for more information");
    00249   IConsolePrint(12,  "");
    00250   IConsoleStdLibRegister();
    00251   IConsoleClearCommand();
    00252   IConsoleHistoryAdd("");
    00253 }
    00254
    00255 void IConsoleClearBuffer()
    00256 {
    00257   uint i;
    00258   for (i = 0; i <= ICON_BUFFER; i++) {
    00259     free(_iconsole_buffer[i]);
    00260     _iconsole_buffer[i] = NULL;
    00261   }
    00262 }
    00263
    00264 static void IConsoleClear()
    00265 {
    00266   free(_iconsole_cmdline.buf);
    00267   IConsoleClearBuffer();
    00268 }
    00269
    00270 static void IConsoleWriteToLogFile(const char *string)
    00271 {
    00272   if (_iconsole_output_file != NULL) {
    00273     /* if there is an console output file ... also print it there */
    00274     fwrite(string, strlen(string), 1, _iconsole_output_file);
    00275     fwrite("\n", 1, 1, _iconsole_output_file);
    00276   }
    00277 }
    00278
    00279 bool CloseConsoleLogIfActive()
    00280 {
    00281   if (_iconsole_output_file != NULL) {
    00282     IConsolePrintF(_icolour_def, "file output complete");
    00283     fclose(_iconsole_output_file);
    00284     _iconsole_output_file = NULL;
    00285     return true;
    00286   }
    00287
    00288   return false;
    00289 }
    00290
    00291 void IConsoleFree()
    00292 {
    00293   IConsoleClear();
    00294   CloseConsoleLogIfActive();
    00295 }
    00296
    00297 void IConsoleResize(Window *w)
    00298 {
    00299   switch (_iconsole_mode) {
    00300     case ICONSOLE_OPENED:
    00301       w->height = _screen.height / 3;
    00302       w->width = _screen.width;
    00303       break;
    00304     case ICONSOLE_FULL:
    00305       w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
    00306       w->width = _screen.width;
    00307       break;
    00308     default: return;
    00309   }
    00310
    00311   MarkWholeScreenDirty();
    00312 }
    00313
    00314 void IConsoleSwitch()
    00315 {
    00316   switch (_iconsole_mode) {
    00317     case ICONSOLE_CLOSED: {
    00318       Window *w = AllocateWindowDesc(&_iconsole_window_desc);
    00319       w->height = _screen.height / 3;
    00320       w->width = _screen.width;
    00321       _iconsole_mode = ICONSOLE_OPENED;
    00322       SetBit(_no_scroll, SCROLL_CON); // override cursor arrows; the gamefield will not scroll
    00323     } break;
    00324     case ICONSOLE_OPENED: case ICONSOLE_FULL:
    00325       DeleteWindowById(WC_CONSOLE, 0);
    00326       _iconsole_mode = ICONSOLE_CLOSED;
    00327       ClrBit(_no_scroll, SCROLL_CON);
    00328       break;
    00329   }
    00330
    00331   MarkWholeScreenDirty();
    00332 }
    00333
    00334 void IConsoleClose() {if (_iconsole_mode == ICONSOLE_OPENED) IConsoleSwitch();}
    00335 void IConsoleOpen()  {if (_iconsole_mode == ICONSOLE_CLOSED) IConsoleSwitch();}
    00336
    00342 static void IConsoleHistoryAdd(const char *cmd)
    00343 {
    00344   free(_iconsole_history[ICON_HISTORY_SIZE - 1]);
    00345
    00346   memmove(&_iconsole_history[1], &_iconsole_history[0], sizeof(_iconsole_history[0]) * (ICON_HISTORY_SIZE - 1));
    00347   _iconsole_history[0] = strdup(cmd);
    00348   IConsoleResetHistoryPos();
    00349 }
    00350
    00355 static void IConsoleHistoryNavigate(int direction)
    00356 {
    00357   int i = _iconsole_historypos + direction;
    00358
    00359   /* watch out for overflows, just wrap around */
    00360   if (i < 0) i = ICON_HISTORY_SIZE - 1;
    00361   if (i >= ICON_HISTORY_SIZE) i = 0;
    00362
    00363   if (direction > 0)
    00364     if (_iconsole_history[i] == NULL) i = 0;
    00365
    00366   if (direction < 0) {
    00367     while (i > 0 && _iconsole_history[i] == NULL) i--;
    00368   }
    00369
    00370   _iconsole_historypos = i;
    00371   IConsoleClearCommand();
    00372   /* copy history to 'command prompt / bash' */
    00373   assert(_iconsole_history[i] != NULL && IsInsideMM(i, 0, ICON_HISTORY_SIZE));
    00374   ttd_strlcpy(_iconsole_cmdline.buf, _iconsole_history[i], _iconsole_cmdline.maxlength);
    00375   UpdateTextBufferSize(&_iconsole_cmdline);
    00376 }
    00377
    00387 void IConsolePrint(uint16 color_code, const char *string)
    00388 {
    00389   char *str;
    00390 #ifdef ENABLE_NETWORK
    00391   if (_redirect_console_to_client != 0) {
    00392     /* Redirect the string to the client */
    00393     SEND_COMMAND(PACKET_SERVER_RCON)(NetworkFindClientStateFromIndex(_redirect_console_to_client), color_code, string);
    00394     return;
    00395   }
    00396 #endif
    00397
    00398   /* Create a copy of the string, strip if of colours and invalid
    00399    * characters and (when applicable) assign it to the console buffer */
    00400   str = strdup(string);
    00401   str_strip_colours(str);
    00402   str_validate(str);
    00403
    00404   if (_network_dedicated) {
    00405     fprintf(stdout, "%s\n", str);
    00406     fflush(stdout);
    00407     IConsoleWriteToLogFile(str);
    00408     free(str); // free duplicated string since it's not used anymore
    00409     return;
    00410   }
    00411
    00412   /* move up all the strings in the buffer one place and do the same for colour
    00413    * to accomodate for the new command/message */
    00414   free(_iconsole_buffer[0]);
    00415   memmove(&_iconsole_buffer[0], &_iconsole_buffer[1], sizeof(_iconsole_buffer[0]) * ICON_BUFFER);
    00416   _iconsole_buffer[ICON_BUFFER] = str;
    00417
    00418   memmove(&_iconsole_cbuffer[0], &_iconsole_cbuffer[1], sizeof(_iconsole_cbuffer[0]) * ICON_BUFFER);
    00419   _iconsole_cbuffer[ICON_BUFFER] = color_code;
    00420
    00421   IConsoleWriteToLogFile(_iconsole_buffer[ICON_BUFFER]);
    00422
    00423   SetWindowDirty(FindWindowById(WC_CONSOLE, 0));
    00424 }
    00425
    00431 void CDECL IConsolePrintF(uint16 color_code, const char *s, ...)
    00432 {
    00433   va_list va;
    00434   char buf[ICON_MAX_STREAMSIZE];
    00435
    00436   va_start(va, s);
    00437   vsnprintf(buf, sizeof(buf), s, va);
    00438   va_end(va);
    00439
    00440   IConsolePrint(color_code, buf);
    00441 }
    00442
    00451 void IConsoleDebug(const char *dbg, const char *string)
    00452 {
    00453   if (_stdlib_developer > 1)
    00454     IConsolePrintF(_icolour_dbg, "dbg: [%s] %s", dbg, string);
    00455 }
    00456
    00462 void IConsoleWarning(const char *string)
    00463 {
    00464   if (_stdlib_developer > 0)
    00465     IConsolePrintF(_icolour_warn, "WARNING: %s", string);
    00466 }
    00467
    00472 void IConsoleError(const char *string)
    00473 {
    00474   IConsolePrintF(_icolour_err, "ERROR: %s", string);
    00475 }
    00476
    00484 bool GetArgumentInteger(uint32 *value, const char *arg)
    00485 {
    00486   char *endptr;
    00487
    00488   if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
    00489     *value = 1;
    00490     return true;
    00491   }
    00492   if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
    00493     *value = 0;
    00494     return true;
    00495   }
    00496
    00497   *value = strtoul(arg, &endptr, 0);
    00498   return arg != endptr;
    00499 }
    00500
    00501 /*  * *************************
    00502     * hooking code            *
    00503     * *************************/
    00504
    00511 static void IConsoleHookAdd(IConsoleHooks *hooks, IConsoleHookTypes type, IConsoleHook *proc)
    00512 {
    00513   if (hooks == NULL || proc == NULL) return;
    00514
    00515   switch (type) {
    00516     case ICONSOLE_HOOK_ACCESS:
    00517       hooks->access = proc;
    00518       break;
    00519     case ICONSOLE_HOOK_PRE_ACTION:
    00520       hooks->pre = proc;
    00521       break;
    00522     case ICONSOLE_HOOK_POST_ACTION:
    00523       hooks->post = proc;
    00524       break;
    00525     default: NOT_REACHED();
    00526   }
    00527 }
    00528
    00537 static bool IConsoleHookHandle(const IConsoleHooks *hooks, IConsoleHookTypes type)
    00538 {
    00539   IConsoleHook *proc = NULL;
    00540   if (hooks == NULL) return false;
    00541
    00542   switch (type) {
    00543     case ICONSOLE_HOOK_ACCESS:
    00544       proc = hooks->access;
    00545       break;
    00546     case ICONSOLE_HOOK_PRE_ACTION:
    00547       proc = hooks->pre;
    00548       break;
    00549     case ICONSOLE_HOOK_POST_ACTION:
    00550       proc = hooks->post;
    00551       break;
    00552     default: NOT_REACHED();
    00553   }
    00554
    00555   return (proc == NULL) ? true : proc();
    00556 }
    00557
    00564 void IConsoleCmdHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00565 {
    00566   IConsoleCmd *cmd = IConsoleCmdGet(name);
    00567   if (cmd == NULL) return;
    00568   IConsoleHookAdd(&cmd->hook, type, proc);
    00569 }
    00570
    00577 void IConsoleVarHookAdd(const char *name, IConsoleHookTypes type, IConsoleHook *proc)
    00578 {
    00579   IConsoleVar *var = IConsoleVarGet(name);
    00580   if (var == NULL) return;
    00581   IConsoleHookAdd(&var->hook, type, proc);
    00582 }
    00583
    00589 #define IConsoleAddSorted(_base, item_new, IConsoleType, type)                 \
    00590 {                                                                              \
    00591   IConsoleType *item, *item_before;                                            \
    00592   /* first command */                                                          \
    00593   if (_base == NULL) {                                                         \
    00594     _base = item_new;                                                          \
    00595     return;                                                                    \
    00596   }                                                                            \
    00597                                                                                \
    00598   item_before = NULL;                                                          \
    00599   item = _base;                                                                \
    00600                                                                                \
    00601   /* BEGIN - Alphabetically insert the commands into the linked list */        \
    00602   while (item != NULL) {                                                       \
    00603     int i = strcmp(item->name, item_new->name);                                \
    00604     if (i == 0) {                                                              \
    00605       IConsoleError(type " with this name already exists; insertion aborted"); \
    00606       free(item_new);                                                          \
    00607       return;                                                                  \
    00608     }                                                                          \
    00609                                                                                \
    00610     if (i > 0) break; /* insert at this position */                            \
    00611                                                                                \
    00612     item_before = item;                                                        \
    00613     item = item->next;                                                         \
    00614   }                                                                            \
    00615                                                                                \
    00616   if (item_before == NULL) {                                                   \
    00617     _base = item_new;                                                          \
    00618   } else {                                                                     \
    00619     item_before->next = item_new;                                              \
    00620   }                                                                            \
    00621                                                                                \
    00622   item_new->next = item;                                                       \
    00623   /* END - Alphabetical insert */                                              \
    00624 }
    00625
    00631 void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc)
    00632 {
    00633   char *new_cmd = strdup(name);
    00634   IConsoleCmd *item_new = MallocT<IConsoleCmd>(1);
    00635
    00636   item_new->next = NULL;
    00637   item_new->proc = proc;
    00638   item_new->name = new_cmd;
    00639
    00640   item_new->hook.access = NULL;
    00641   item_new->hook.pre = NULL;
    00642   item_new->hook.post = NULL;
    00643
    00644   IConsoleAddSorted(_iconsole_cmds, item_new, IConsoleCmd, "a command");
    00645 }
    00646
    00652 IConsoleCmd *IConsoleCmdGet(const char *name)
    00653 {
    00654   IConsoleCmd *item;
    00655
    00656   for (item = _iconsole_cmds; item != NULL; item = item->next) {
    00657     if (strcmp(item->name, name) == 0) return item;
    00658   }
    00659   return NULL;
    00660 }
    00661
    00667 void IConsoleAliasRegister(const char *name, const char *cmd)
    00668 {
    00669   char *new_alias = strdup(name);
    00670   char *cmd_aliased = strdup(cmd);
    00671   IConsoleAlias *item_new = MallocT<IConsoleAlias>(1);
    00672
    00673   item_new->next = NULL;
    00674   item_new->cmdline = cmd_aliased;
    00675   item_new->name = new_alias;
    00676
    00677   IConsoleAddSorted(_iconsole_aliases, item_new, IConsoleAlias, "an alias");
    00678 }
    00679
    00685 IConsoleAlias *IConsoleAliasGet(const char *name)
    00686 {
    00687   IConsoleAlias* item;
    00688
    00689   for (item = _iconsole_aliases; item != NULL; item = item->next) {
    00690     if (strcmp(item->name, name) == 0) return item;
    00691   }
    00692
    00693   return NULL;
    00694 }
    00695
    00697 static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
    00698 {
    00699   int len = min(ICON_MAX_STREAMSIZE - bufpos, (uint)strlen(src));
    00700   strecpy(dst, src, dst + len - 1);
    00701
    00702   return len;
    00703 }
    00704
    00712 static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
    00713 {
    00714   const char *cmdptr;
    00715   char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
    00716   uint i;
    00717   uint a_index, astream_i;
    00718
    00719   memset(&aliases, 0, sizeof(aliases));
    00720   memset(&aliasstream, 0, sizeof(aliasstream));
    00721
    00722   if (_stdlib_con_developer)
    00723     IConsolePrintF(_icolour_dbg, "condbg: requested command is an alias; parsing...");
    00724
    00725   aliases[0] = aliasstream;
    00726   for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
    00727     if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
    00728
    00729     switch (*cmdptr) {
    00730     case '\'': // ' will double for ""
    00731       aliasstream[astream_i++] = '"';
    00732       break;
    00733     case ';': // Cmd seperator, start new command
    00734       aliasstream[astream_i] = '\0';
    00735       aliases[++a_index] = &aliasstream[++astream_i];
    00736       cmdptr++;
    00737       break;
    00738     case '%': // Some or all parameters
    00739       cmdptr++;
    00740       switch (*cmdptr) {
    00741       case '+': { // All parameters seperated: "[param 1]" "[param 2]"
    00742         for (i = 0; i != tokencount; i++) {
    00743           aliasstream[astream_i++] = '"';
    00744           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00745           aliasstream[astream_i++] = '"';
    00746           aliasstream[astream_i++] = ' ';
    00747         }
    00748       } break;
    00749       case '!': { // Merge the parameters to one: "[param 1] [param 2] [param 3...]"
    00750         aliasstream[astream_i++] = '"';
    00751         for (i = 0; i != tokencount; i++) {
    00752           astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
    00753           aliasstream[astream_i++] = ' ';
    00754         }
    00755         aliasstream[astream_i++] = '"';
    00756
    00757       } break;
    00758         default: { // One specific parameter: %A = [param 1] %B = [param 2] ...
    00759         int param = *cmdptr - 'A';
    00760
    00761         if (param < 0 || param >= tokencount) {
    00762           IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
    00763           IConsolePrintF(_icolour_warn, "Usage of alias '%s': %s", alias->name, alias->cmdline);
    00764           return;
    00765         }
    00766
    00767         aliasstream[astream_i++] = '"';
    00768         astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
    00769         aliasstream[astream_i++] = '"';
    00770       } break;
    00771       } break;
    00772
    00773     default:
    00774       aliasstream[astream_i++] = *cmdptr;
    00775       break;
    00776     }
    00777   }
    00778
    00779   for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
    00780 }
    00781
    00791 void IConsoleVarStringRegister(const char *name, void *addr, uint32 size, const char *help)
    00792 {
    00793   IConsoleVar *var;
    00794   IConsoleVarRegister(name, addr, ICONSOLE_VAR_STRING, help);
    00795   var = IConsoleVarGet(name);
    00796   var->size = size;
    00797 }
    00798
    00806 void IConsoleVarRegister(const char *name, void *addr, IConsoleVarTypes type, const char *help)
    00807 {
    00808   char *new_cmd = strdup(name);
    00809   IConsoleVar *item_new = MallocT<IConsoleVar>(1);
    00810
    00811   item_new->help = (help != NULL) ? strdup(help) : NULL;
    00812
    00813   item_new->next = NULL;
    00814   item_new->name = new_cmd;
    00815   item_new->addr = addr;
    00816   item_new->proc = NULL;
    00817   item_new->type = type;
    00818
    00819   item_new->hook.access = NULL;
    00820   item_new->hook.pre = NULL;
    00821   item_new->hook.post = NULL;
    00822
    00823   IConsoleAddSorted(_iconsole_vars, item_new, IConsoleVar, "a variable");
    00824 }
    00825
    00831 IConsoleVar *IConsoleVarGet(const char *name)
    00832 {
    00833   IConsoleVar *item;
    00834   for (item = _iconsole_vars; item != NULL; item = item->next) {
    00835     if (strcmp(item->name, name) == 0) return item;
    00836   }
    00837
    00838   return NULL;
    00839 }
    00840
    00846 static void IConsoleVarSetValue(const IConsoleVar *var, uint32 value)
    00847 {
    00848   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00849   switch (var->type) {
    00850     case ICONSOLE_VAR_BOOLEAN:
    00851       *(bool*)var->addr = (value != 0);
    00852       break;
    00853     case ICONSOLE_VAR_BYTE:
    00854       *(byte*)var->addr = (byte)value;
    00855       break;
    00856     case ICONSOLE_VAR_UINT16:
    00857       *(uint16*)var->addr = (uint16)value;
    00858       break;
    00859     case ICONSOLE_VAR_INT16:
    00860       *(int16*)var->addr = (int16)value;
    00861       break;
    00862     case ICONSOLE_VAR_UINT32:
    00863       *(uint32*)var->addr = (uint32)value;
    00864       break;
    00865     case ICONSOLE_VAR_INT32:
    00866       *(int32*)var->addr = (int32)value;
    00867       break;
    00868     default: NOT_REACHED();
    00869   }
    00870
    00871   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00872   IConsoleVarPrintSetValue(var);
    00873 }
    00874
    00881 static void IConsoleVarSetStringvalue(const IConsoleVar *var, const char *value)
    00882 {
    00883   if (var->type != ICONSOLE_VAR_STRING || var->addr == NULL) return;
    00884
    00885   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_PRE_ACTION);
    00886   ttd_strlcpy((char*)var->addr, value, var->size);
    00887   IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_POST_ACTION);
    00888   IConsoleVarPrintSetValue(var); // print out the new value, giving feedback
    00889   return;
    00890 }
    00891
    00897 static uint32 IConsoleVarGetValue(const IConsoleVar *var)
    00898 {
    00899   uint32 result = 0;
    00900
    00901   switch (var->type) {
    00902     case ICONSOLE_VAR_BOOLEAN:
    00903       result = *(bool*)var->addr;
    00904       break;
    00905     case ICONSOLE_VAR_BYTE:
    00906       result = *(byte*)var->addr;
    00907       break;
    00908     case ICONSOLE_VAR_UINT16:
    00909       result = *(uint16*)var->addr;
    00910       break;
    00911     case ICONSOLE_VAR_INT16:
    00912       result = *(int16*)var->addr;
    00913       break;
    00914     case ICONSOLE_VAR_UINT32:
    00915       result = *(uint32*)var->addr;
    00916       break;
    00917     case ICONSOLE_VAR_INT32:
    00918       result = *(int32*)var->addr;
    00919       break;
    00920     default: NOT_REACHED();
    00921   }
    00922   return result;
    00923 }
    00924
    00929 static char *IConsoleVarGetStringValue(const IConsoleVar *var)
    00930 {
    00931   static char tempres[50];
    00932   char *value = tempres;
    00933
    00934   switch (var->type) {
    00935     case ICONSOLE_VAR_BOOLEAN:
    00936       snprintf(tempres, sizeof(tempres), "%s", (*(bool*)var->addr) ? "on" : "off");
    00937       break;
    00938     case ICONSOLE_VAR_BYTE:
    00939       snprintf(tempres, sizeof(tempres), "%u", *(byte*)var->addr);
    00940       break;
    00941     case ICONSOLE_VAR_UINT16:
    00942       snprintf(tempres, sizeof(tempres), "%u", *(uint16*)var->addr);
    00943       break;
    00944     case ICONSOLE_VAR_UINT32:
    00945       snprintf(tempres, sizeof(tempres), "%u",  *(uint32*)var->addr);
    00946       break;
    00947     case ICONSOLE_VAR_INT16:
    00948       snprintf(tempres, sizeof(tempres), "%i", *(int16*)var->addr);
    00949       break;
    00950     case ICONSOLE_VAR_INT32:
    00951       snprintf(tempres, sizeof(tempres), "%i",  *(int32*)var->addr);
    00952       break;
    00953     case ICONSOLE_VAR_STRING:
    00954       value = (char*)var->addr;
    00955       break;
    00956     default: NOT_REACHED();
    00957   }
    00958
    00959   return value;
    00960 }
    00961
    00965 void IConsoleVarPrintGetValue(const IConsoleVar *var)
    00966 {
    00967   char *value;
    00968   /* Some variables need really specific handling, handle this in its
    00969    * callback function */
    00970   if (var->proc != NULL) {
    00971     var->proc(0, NULL);
    00972     return;
    00973   }
    00974
    00975   value = IConsoleVarGetStringValue(var);
    00976   IConsolePrintF(_icolour_warn, "Current value for '%s' is:  %s", var->name, value);
    00977 }
    00978
    00983 void IConsoleVarPrintSetValue(const IConsoleVar *var)
    00984 {
    00985   char *value = IConsoleVarGetStringValue(var);
    00986   IConsolePrintF(_icolour_warn, "'%s' changed to:  %s", var->name, value);
    00987 }
    00988
    00996 void IConsoleVarExec(const IConsoleVar *var, byte tokencount, char *token[ICON_TOKEN_COUNT])
    00997 {
    00998   const char *tokenptr = token[0];
    00999   byte t_index = tokencount;
    01000   uint32 value;
    01001
    01002   if (_stdlib_con_developer)
    01003     IConsolePrintF(_icolour_dbg, "condbg: requested command is a variable");
    01004
    01005   if (tokencount == 0) { /* Just print out value */
    01006     IConsoleVarPrintGetValue(var);
    01007     return;
    01008   }
    01009
    01010   /* Use of assignment sign is not mandatory but supported, so just 'ignore it appropiately' */
    01011   if (strcmp(tokenptr, "=") == 0) tokencount--;
    01012
    01013   if (tokencount == 1) {
    01014     /* Some variables need really special handling, handle it in their callback procedure */
    01015     if (var->proc != NULL) {
    01016       var->proc(tokencount, &token[t_index - tokencount]); // set the new value
    01017       return;
    01018     }
    01019     /* Strings need special processing. No need to convert the argument to
    01020      * an integer value, just copy over the argument on a one-by-one basis */
    01021     if (var->type == ICONSOLE_VAR_STRING) {
    01022       IConsoleVarSetStringvalue(var, token[t_index - tokencount]);
    01023       return;
    01024     } else if (GetArgumentInteger(&value, token[t_index - tokencount])) {
    01025       IConsoleVarSetValue(var, value);
    01026       return;
    01027     }
    01028
    01029     /* Increase or decrease the value by one. This of course can only happen to 'number' types */
    01030     if (strcmp(tokenptr, "++") == 0 && var->type != ICONSOLE_VAR_STRING) {
    01031       IConsoleVarSetValue(var, IConsoleVarGetValue(var) + 1);
    01032       return;
    01033     }
    01034
    01035     if (strcmp(tokenptr, "--") == 0 && var->type != ICONSOLE_VAR_STRING) {
    01036       IConsoleVarSetValue(var, IConsoleVarGetValue(var) - 1);
    01037       return;
    01038     }
    01039   }
    01040
    01041   IConsoleError("invalid variable assignment");
    01042 }
    01043
    01050 void IConsoleVarProcAdd(const char *name, IConsoleCmdProc *proc)
    01051 {
    01052   IConsoleVar *var = IConsoleVarGet(name);
    01053   if (var == NULL) return;
    01054   var->proc = proc;
    01055 }
    01056
    01062 void IConsoleCmdExec(const char *cmdstr)
    01063 {
    01064   IConsoleCmd   *cmd    = NULL;
    01065   IConsoleAlias *alias  = NULL;
    01066   IConsoleVar   *var    = NULL;
    01067
    01068   const char *cmdptr;
    01069   char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
    01070   uint t_index, tstream_i;
    01071
    01072   bool longtoken = false;
    01073   bool foundtoken = false;
    01074
    01075   if (cmdstr[0] == '#') return; // comments
    01076
    01077   for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
    01078     if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
    01079       IConsoleError("command contains malformed characters, aborting");
    01080       IConsolePrintF(_icolour_err, "ERROR: command was: '%s'", cmdstr);
    01081       return;
    01082     }
    01083   }
    01084
    01085   if (_stdlib_con_developer)
    01086     IConsolePrintF(_icolour_dbg, "condbg: executing cmdline: '%s'", cmdstr);
    01087
    01088   memset(&tokens, 0, sizeof(tokens));
    01089   memset(&tokenstream, 0, sizeof(tokenstream));
    01090
    01091   /* 1. Split up commandline into tokens, seperated by spaces, commands
    01092    * enclosed in "" are taken as one token. We can only go as far as the amount
    01093    * of characters in our stream or the max amount of tokens we can handle */
    01094   for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
    01095     if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
    01096
    01097     switch (*cmdptr) {
    01098     case ' ': /* Token seperator */
    01099       if (!foundtoken) break;
    01100
    01101       if (longtoken) {
    01102         tokenstream[tstream_i] = *cmdptr;
    01103       } else {
    01104         tokenstream[tstream_i] = '\0';
    01105         foundtoken = false;
    01106       }
    01107
    01108       tstream_i++;
    01109       break;
    01110     case '"': // Tokens enclosed in "" are one token
    01111       longtoken = !longtoken;
    01112       break;
    01113     case '\\': // Escape character for ""
    01114       if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
    01115         tokenstream[tstream_i++] = *++cmdptr;
    01116         break;
    01117       }
    01118       /* fallthrough */
    01119     default: // Normal character
    01120       tokenstream[tstream_i++] = *cmdptr;
    01121
    01122       if (!foundtoken) {
    01123         tokens[t_index++] = &tokenstream[tstream_i - 1];
    01124         foundtoken = true;
    01125       }
    01126       break;
    01127     }
    01128   }
    01129
    01130   if (_stdlib_con_developer) {
    01131     uint i;
    01132
    01133     for (i = 0; tokens[i] != NULL; i++) {
    01134       IConsolePrintF(_icolour_dbg, "condbg: token %d is: '%s'", i, tokens[i]);
    01135     }
    01136   }
    01137
    01138   if (tokens[0] == '\0') return; // don't execute empty commands
    01139   /* 2. Determine type of command (cmd, alias or variable) and execute
    01140    * First try commands, then aliases, and finally variables. Execute
    01141    * the found action taking into account its hooking code
    01142    */
    01143   cmd = IConsoleCmdGet(tokens[0]);
    01144   if (cmd != NULL) {
    01145     if (IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_ACCESS)) {
    01146       IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_PRE_ACTION);
    01147       if (cmd->proc(t_index, tokens)) { // index started with 0
    01148         IConsoleHookHandle(&cmd->hook, ICONSOLE_HOOK_POST_ACTION);
    01149       } else {
    01150         cmd->proc(0, NULL); // if command failed, give help
    01151       }
    01152     }
    01153     return;
    01154   }
    01155
    01156   t_index--; // ignore the variable-name for comfort for both aliases and variaables
    01157   alias = IConsoleAliasGet(tokens[0]);
    01158   if (alias != NULL) {
    01159     IConsoleAliasExec(alias, t_index, &tokens[1]);
    01160     return;
    01161   }
    01162
    01163   var = IConsoleVarGet(tokens[0]);
    01164   if (var != NULL) {
    01165     if (IConsoleHookHandle(&var->hook, ICONSOLE_HOOK_ACCESS)) {
    01166       IConsoleVarExec(var, t_index, &tokens[1]);
    01167     }
    01168     return;
    01169   }
    01170
    01171   IConsoleError("command or variable not found");
    01172 }