console.cpp

Go to the documentation of this file.
00001 /* $Id: console.cpp 20977 2010-10-17 17:50:40Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "console_internal.h"
00014 #include "network/network.h"
00015 #include "network/network_func.h"
00016 #include "network/network_admin.h"
00017 #include "debug.h"
00018 #include "console_func.h"
00019 #include "settings_type.h"
00020 
00021 #include <stdarg.h>
00022 
00023 #define ICON_BUFFER 79
00024 #define ICON_HISTORY_SIZE 20
00025 #define ICON_LINE_HEIGHT 12
00026 #define ICON_RIGHT_BORDERWIDTH 10
00027 #define ICON_BOTTOM_BORDERWIDTH 12
00028 #define ICON_MAX_ALIAS_LINES 40
00029 #define ICON_TOKEN_COUNT 20
00030 
00031 /* console parser */
00032 IConsoleCmd   *_iconsole_cmds;    
00033 IConsoleAlias *_iconsole_aliases; 
00034 
00035 FILE *_iconsole_output_file;
00036 
00037 void IConsoleInit()
00038 {
00039   _iconsole_output_file = NULL;
00040 #ifdef ENABLE_NETWORK /* Initialize network only variables */
00041   _redirect_console_to_client = INVALID_CLIENT_ID;
00042   _redirect_console_to_admin  = INVALID_ADMIN_ID;
00043 #endif
00044 
00045   IConsoleGUIInit();
00046 
00047   IConsoleStdLibRegister();
00048 }
00049 
00050 static void IConsoleWriteToLogFile(const char *string)
00051 {
00052   if (_iconsole_output_file != NULL) {
00053     /* if there is an console output file ... also print it there */
00054     const char *header = GetLogPrefix();
00055     if ((strlen(header) != 0 && fwrite(header, strlen(header), 1, _iconsole_output_file) != 1) ||
00056         fwrite(string, strlen(string), 1, _iconsole_output_file) != 1 ||
00057         fwrite("\n", 1, 1, _iconsole_output_file) != 1) {
00058       fclose(_iconsole_output_file);
00059       _iconsole_output_file = NULL;
00060       IConsolePrintF(CC_DEFAULT, "cannot write to log file");
00061     }
00062   }
00063 }
00064 
00065 bool CloseConsoleLogIfActive()
00066 {
00067   if (_iconsole_output_file != NULL) {
00068     IConsolePrintF(CC_DEFAULT, "file output complete");
00069     fclose(_iconsole_output_file);
00070     _iconsole_output_file = NULL;
00071     return true;
00072   }
00073 
00074   return false;
00075 }
00076 
00077 void IConsoleFree()
00078 {
00079   IConsoleGUIFree();
00080   CloseConsoleLogIfActive();
00081 }
00082 
00092 void IConsolePrint(ConsoleColour colour_code, const char *string)
00093 {
00094   char *str;
00095 #ifdef ENABLE_NETWORK
00096   if (_redirect_console_to_client != INVALID_CLIENT_ID) {
00097     /* Redirect the string to the client */
00098     NetworkServerSendRcon(_redirect_console_to_client, colour_code, string);
00099     return;
00100   }
00101 
00102   if (_redirect_console_to_admin != INVALID_ADMIN_ID) {
00103     NetworkServerSendAdminRcon(_redirect_console_to_admin, colour_code, string);
00104     return;
00105   }
00106 #endif
00107 
00108   /* Create a copy of the string, strip if of colours and invalid
00109    * characters and (when applicable) assign it to the console buffer */
00110   str = strdup(string);
00111   str_strip_colours(str);
00112   str_validate(str, str + strlen(str));
00113 
00114   if (_network_dedicated) {
00115 #ifdef ENABLE_NETWORK
00116     NetworkAdminConsole("console", str);
00117 #endif /* ENABLE_NETWORK */
00118     fprintf(stdout, "%s%s\n", GetLogPrefix(), str);
00119     fflush(stdout);
00120     IConsoleWriteToLogFile(str);
00121     free(str); // free duplicated string since it's not used anymore
00122     return;
00123   }
00124 
00125   IConsoleWriteToLogFile(str);
00126   IConsoleGUIPrint(colour_code, str);
00127 }
00128 
00134 void CDECL IConsolePrintF(ConsoleColour colour_code, const char *format, ...)
00135 {
00136   va_list va;
00137   char buf[ICON_MAX_STREAMSIZE];
00138 
00139   va_start(va, format);
00140   vsnprintf(buf, sizeof(buf), format, va);
00141   va_end(va);
00142 
00143   IConsolePrint(colour_code, buf);
00144 }
00145 
00154 void IConsoleDebug(const char *dbg, const char *string)
00155 {
00156   if (_settings_client.gui.developer <= 1) return;
00157   IConsolePrintF(CC_DEBUG, "dbg: [%s] %s", dbg, string);
00158 }
00159 
00165 void IConsoleWarning(const char *string)
00166 {
00167   if (_settings_client.gui.developer == 0) return;
00168   IConsolePrintF(CC_WARNING, "WARNING: %s", string);
00169 }
00170 
00175 void IConsoleError(const char *string)
00176 {
00177   IConsolePrintF(CC_ERROR, "ERROR: %s", string);
00178 }
00179 
00187 bool GetArgumentInteger(uint32 *value, const char *arg)
00188 {
00189   char *endptr;
00190 
00191   if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
00192     *value = 1;
00193     return true;
00194   }
00195   if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
00196     *value = 0;
00197     return true;
00198   }
00199 
00200   *value = strtoul(arg, &endptr, 0);
00201   return arg != endptr;
00202 }
00203 
00209 template<class T>
00210 void IConsoleAddSorted(T **base, T *item_new)
00211 {
00212   if (*base == NULL) {
00213     *base = item_new;
00214     return;
00215   }
00216 
00217   T *item_before = NULL;
00218   T *item = *base;
00219   /* The list is alphabetically sorted, insert the new item at the correct location */
00220   while (item != NULL) {
00221     if (strcmp(item->name, item_new->name) > 0) break; // insert here
00222 
00223     item_before = item;
00224     item = item->next;
00225   }
00226 
00227   if (item_before == NULL) {
00228     *base = item_new;
00229   } else {
00230     item_before->next = item_new;
00231   }
00232 
00233   item_new->next = item;
00234 }
00235 
00241 char *RemoveUnderscores(char *name)
00242 {
00243   char *q = name;
00244   for (const char *p = name; *p != '\0'; p++) {
00245     if (*p != '_') *q++ = *p;
00246   }
00247   *q = '\0';
00248   return name;
00249 }
00250 
00256 void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook)
00257 {
00258   IConsoleCmd *item_new = MallocT<IConsoleCmd>(1);
00259   item_new->name = RemoveUnderscores(strdup(name));
00260   item_new->next = NULL;
00261   item_new->proc = proc;
00262   item_new->hook = hook;
00263 
00264   IConsoleAddSorted(&_iconsole_cmds, item_new);
00265 }
00266 
00272 IConsoleCmd *IConsoleCmdGet(const char *name)
00273 {
00274   IConsoleCmd *item;
00275 
00276   for (item = _iconsole_cmds; item != NULL; item = item->next) {
00277     if (strcmp(item->name, name) == 0) return item;
00278   }
00279   return NULL;
00280 }
00281 
00287 void IConsoleAliasRegister(const char *name, const char *cmd)
00288 {
00289   if (IConsoleAliasGet(name) != NULL) {
00290     IConsoleError("an alias with this name already exists; insertion aborted");
00291     return;
00292   }
00293 
00294   char *new_alias = RemoveUnderscores(strdup(name));
00295   char *cmd_aliased = strdup(cmd);
00296   IConsoleAlias *item_new = MallocT<IConsoleAlias>(1);
00297 
00298   item_new->next = NULL;
00299   item_new->cmdline = cmd_aliased;
00300   item_new->name = new_alias;
00301 
00302   IConsoleAddSorted(&_iconsole_aliases, item_new);
00303 }
00304 
00310 IConsoleAlias *IConsoleAliasGet(const char *name)
00311 {
00312   IConsoleAlias *item;
00313 
00314   for (item = _iconsole_aliases; item != NULL; item = item->next) {
00315     if (strcmp(item->name, name) == 0) return item;
00316   }
00317 
00318   return NULL;
00319 }
00320 
00322 static inline int IConsoleCopyInParams(char *dst, const char *src, uint bufpos)
00323 {
00324   /* len is the amount of bytes to add excluding the '\0'-termination */
00325   int len = min(ICON_MAX_STREAMSIZE - bufpos - 1, (uint)strlen(src));
00326   strecpy(dst, src, dst + len);
00327 
00328   return len;
00329 }
00330 
00338 static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT])
00339 {
00340   const char *cmdptr;
00341   char *aliases[ICON_MAX_ALIAS_LINES], aliasstream[ICON_MAX_STREAMSIZE];
00342   uint i;
00343   uint a_index, astream_i;
00344 
00345   memset(&aliases, 0, sizeof(aliases));
00346   memset(&aliasstream, 0, sizeof(aliasstream));
00347 
00348   DEBUG(console, 6, "Requested command is an alias; parsing...");
00349 
00350   aliases[0] = aliasstream;
00351   for (cmdptr = alias->cmdline, a_index = 0, astream_i = 0; *cmdptr != '\0'; cmdptr++) {
00352     if (a_index >= lengthof(aliases) || astream_i >= lengthof(aliasstream)) break;
00353 
00354     switch (*cmdptr) {
00355       case '\'': // ' will double for ""
00356         aliasstream[astream_i++] = '"';
00357         break;
00358 
00359       case ';': // Cmd seperator, start new command
00360         aliasstream[astream_i] = '\0';
00361         aliases[++a_index] = &aliasstream[++astream_i];
00362         cmdptr++;
00363         break;
00364 
00365       case '%': // Some or all parameters
00366         cmdptr++;
00367         switch (*cmdptr) {
00368           case '+': { // All parameters seperated: "[param 1]" "[param 2]"
00369             for (i = 0; i != tokencount; i++) {
00370               aliasstream[astream_i++] = '"';
00371               astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
00372               aliasstream[astream_i++] = '"';
00373               aliasstream[astream_i++] = ' ';
00374             }
00375             break;
00376           }
00377 
00378           case '!': { // Merge the parameters to one: "[param 1] [param 2] [param 3...]"
00379             aliasstream[astream_i++] = '"';
00380             for (i = 0; i != tokencount; i++) {
00381               astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[i], astream_i);
00382               aliasstream[astream_i++] = ' ';
00383             }
00384             aliasstream[astream_i++] = '"';
00385             break;
00386           }
00387 
00388           default: { // One specific parameter: %A = [param 1] %B = [param 2] ...
00389             int param = *cmdptr - 'A';
00390 
00391             if (param < 0 || param >= tokencount) {
00392               IConsoleError("too many or wrong amount of parameters passed to alias, aborting");
00393               IConsolePrintF(CC_WARNING, "Usage of alias '%s': %s", alias->name, alias->cmdline);
00394               return;
00395             }
00396 
00397             aliasstream[astream_i++] = '"';
00398             astream_i += IConsoleCopyInParams(&aliasstream[astream_i], tokens[param], astream_i);
00399             aliasstream[astream_i++] = '"';
00400             break;
00401           }
00402         }
00403         break;
00404 
00405       default:
00406         aliasstream[astream_i++] = *cmdptr;
00407         break;
00408     }
00409   }
00410 
00411   for (i = 0; i <= a_index; i++) IConsoleCmdExec(aliases[i]); // execute each alias in turn
00412 }
00413 
00419 void IConsoleCmdExec(const char *cmdstr)
00420 {
00421   const char *cmdptr;
00422   char *tokens[ICON_TOKEN_COUNT], tokenstream[ICON_MAX_STREAMSIZE];
00423   uint t_index, tstream_i;
00424 
00425   bool longtoken = false;
00426   bool foundtoken = false;
00427 
00428   if (cmdstr[0] == '#') return; // comments
00429 
00430   for (cmdptr = cmdstr; *cmdptr != '\0'; cmdptr++) {
00431     if (!IsValidChar(*cmdptr, CS_ALPHANUMERAL)) {
00432       IConsoleError("command contains malformed characters, aborting");
00433       IConsolePrintF(CC_ERROR, "ERROR: command was: '%s'", cmdstr);
00434       return;
00435     }
00436   }
00437 
00438   DEBUG(console, 4, "Executing cmdline: '%s'", cmdstr);
00439 
00440   memset(&tokens, 0, sizeof(tokens));
00441   memset(&tokenstream, 0, sizeof(tokenstream));
00442 
00443   /* 1. Split up commandline into tokens, seperated by spaces, commands
00444    * enclosed in "" are taken as one token. We can only go as far as the amount
00445    * of characters in our stream or the max amount of tokens we can handle */
00446   for (cmdptr = cmdstr, t_index = 0, tstream_i = 0; *cmdptr != '\0'; cmdptr++) {
00447     if (t_index >= lengthof(tokens) || tstream_i >= lengthof(tokenstream)) break;
00448 
00449     switch (*cmdptr) {
00450     case ' ': // Token seperator
00451       if (!foundtoken) break;
00452 
00453       if (longtoken) {
00454         tokenstream[tstream_i] = *cmdptr;
00455       } else {
00456         tokenstream[tstream_i] = '\0';
00457         foundtoken = false;
00458       }
00459 
00460       tstream_i++;
00461       break;
00462     case '"': // Tokens enclosed in "" are one token
00463       longtoken = !longtoken;
00464       if (!foundtoken) {
00465         tokens[t_index++] = &tokenstream[tstream_i];
00466         foundtoken = true;
00467       }
00468       break;
00469     case '\\': // Escape character for ""
00470       if (cmdptr[1] == '"' && tstream_i + 1 < lengthof(tokenstream)) {
00471         tokenstream[tstream_i++] = *++cmdptr;
00472         break;
00473       }
00474       /* FALL THROUGH */
00475     default: // Normal character
00476       tokenstream[tstream_i++] = *cmdptr;
00477 
00478       if (!foundtoken) {
00479         tokens[t_index++] = &tokenstream[tstream_i - 1];
00480         foundtoken = true;
00481       }
00482       break;
00483     }
00484   }
00485 
00486   for (uint i = 0; tokens[i] != NULL; i++) {
00487     DEBUG(console, 8, "Token %d is: '%s'", i, tokens[i]);
00488   }
00489 
00490   if (tokens[0] == '\0') return; // don't execute empty commands
00491   /* 2. Determine type of command (cmd or alias) and execute
00492    * First try commands, then aliases. Execute
00493    * the found action taking into account its hooking code
00494    */
00495   RemoveUnderscores(tokens[0]);
00496   IConsoleCmd *cmd = IConsoleCmdGet(tokens[0]);
00497   if (cmd != NULL) {
00498     ConsoleHookResult chr = (cmd->hook == NULL ? CHR_ALLOW : cmd->hook(true));
00499     switch (chr) {
00500       case CHR_ALLOW:
00501         if (!cmd->proc(t_index, tokens)) { // index started with 0
00502           cmd->proc(0, NULL); // if command failed, give help
00503         }
00504         return;
00505 
00506       case CHR_DISALLOW: return;
00507       case CHR_HIDE: break;
00508     }
00509   }
00510 
00511   t_index--;
00512   IConsoleAlias *alias = IConsoleAliasGet(tokens[0]);
00513   if (alias != NULL) {
00514     IConsoleAliasExec(alias, t_index, &tokens[1]);
00515     return;
00516   }
00517 
00518   IConsoleError("command not found");
00519 }

Generated on Fri Dec 31 17:15:30 2010 for OpenTTD by  doxygen 1.6.1