# console.cpp

Go to the documentation of this file.

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