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 }

Generated on Wed Oct 1 17:03:20 2008 for openttd by  doxygen 1.5.6