📄 interpreter.cc
字号:
// a class to keep a list of commands and execute them one at a time#include "mcl.h"#include "cui.h"#include "Option.h"#include "MessageWindow.h"#include "Interpreter.h"#include "Shell.h"#include "Action.h"#include "Alias.h"#include "Chat.h"#include "Session.h"Interpreter interpreter;extern MUD *lastMud;bool actions_disabled, aliases_disabled, macros_disabled;// Pick off one argument, optionally smashing case and place it in buf// Eat whitespace, respect "" and ''const char *one_argument (const char *argument, char *buf, bool smash_case) { char end; while(isspace(*argument)) argument++; if (*argument == '\'' || *argument == '\"') end = *argument++; else end = NUL; while(*argument && (end ? *argument != end : !isspace(*argument))) { *buf++ = smash_case ? tolower(*argument) : *argument; argument++; } *buf++ = NUL; if (*argument && end) argument++; while(isspace(*argument)) argument++; return argument;}void Interpreter::execute() { int count = 0; while(commands.count()) { String line = *commands[0]; commands.remove(commands[0]); if (++count > 100) { output->printf ("Recursing alias? Next command would be \"%s\".\n", (const char*)line); while(commands.count()) commands.remove(commands[0]); break; } char out_buf[MAX_INPUT_BUF]; const char *t = out_buf; if (!(embed_interp->run_quietly("sys/send", line, out_buf))) t = line; if (t[0] == commandCharacter) mclCommand (t+ 1); else if (currentSession) { currentSession->writeMUD(t); } else status->setf ("You are not connected. Use Alt-O to connect to a MUD."); }}// Legal speedwalk stringsconst char legal_standard_speedwalk[] = "nsewud";const char legal_extended_speedwalk[] = "nsewudhjkl";#define MAX_SPEEDWALK_REPEAT 99 // just in case someone typos '244n'#define ADD2(s) *pc2++ = s[0]; *pc2++ = s[1];// Expand possible speedwalk stringvoid Interpreter::expandSpeedwalk(const char *s, int flags){ const char *pc; bool try_speedwalk = config->getOption(opt_speedwalk); const char *legal_speedwalk; if (s[0] == config->getOption(opt_speedwalk_character)) { try_speedwalk = true; legal_speedwalk = legal_extended_speedwalk; s++; } else legal_speedwalk = legal_standard_speedwalk; if (try_speedwalk) { for (pc = s; *pc; pc++) if (!isdigit(*pc) && !strchr (legal_speedwalk, *pc)) break; // the harcoded "News" will probably be a config option if (!*pc && strcasecmp(s,"news") && (strchr(legal_speedwalk, *(pc-1)))) // it IS a speedwalk string { int repeat = 0; for (pc = s; *pc; pc++) if (isdigit(*pc)) repeat = 10 * repeat + *pc - '0'; else { repeat = max(1,min(MAX_SPEEDWALK_REPEAT,repeat)); char *walk = (char*)alloca(repeat*3+1); char *pc2 = walk; while(repeat--) { switch (*pc) { case 'h': ADD2("nw"); break; case 'j': ADD2("ne"); break; case 'k': ADD2("sw"); break; case 'l': ADD2("se"); break; default: *pc2++ = *pc; } *pc2++ = NUL; add (walk, EXPAND_NONE); } repeat = 0; } return; } } add(s,flags & ~EXPAND_SPEEDWALK);}const char* Interpreter::expandVariables(const char *s) { if (!strchr(s, '%')) return s; else { StaticBuffer buf(MAX_MUD_BUF); char *out; for (out = buf; *s; s++) { struct tm *t = NULL; if (*s == '%' && *(s+1)) { switch(*++s) { // @@ change this to snprintf case 'h': // remote hostname out += sprintf(out, "%s", currentSession ? currentSession->mud.getHostname() : ""); break; case 'p': // remote portname out += sprintf(out, "%d", currentSession ? currentSession->mud.getPort() : 0); break; case 'n': out += sprintf(out, "%s", currentSession ?~ currentSession->mud.name : ""); break; case 'P': out += sprintf(out, "%d", currentSession ? currentSession->getLocalPort(): 0); break; case 'f': // mudFTP port out += sprintf(out, "%d", currentSession ? currentSession->mud.getPort() + 6 : 0); break; // strftime uses some slightly different characters #define TIME(my_char,their_char) case my_char: \ { \ char fmt[3] = { '%', their_char, NUL }; \ if (!t) t= localtime(¤t_time); \ out += strftime(out, (buf+MAX_MUD_BUF)-out, fmt, t); \ break; \ } TIME('H', 'H'); // Hour TIME('m', 'M'); // Minute TIME('M', 'b'); // Abbreviated month name TIME('w', 'a'); // Abbreviated weekday name TIME('t', 'c'); // Preferred date and time representation TIME('d', 'd'); // Day of the month TIME('y', 'y'); // Year (last two digits) TIME('Y', 'Y'); // Full year TIME('o', 'm'); // month number, 01-11 case '%': // Just one % out += sprintf(out, "%%"); break; // Need something like username/password here default: *out++ = *s; } } else *out++ = *s; } *out = NUL; return buf; }}// %variables// speedwalk (can not recurse)// aliases (can recurse)// ;// Results from aliases: 2,3// Command// Expand stuff in this line before it gets sent to the MUDvoid Interpreter::add(const char *s, int flags, bool back) { if (s[0] == config->getOption(opt_escape_character)) { // short circuit if (back) commands.insert(new String(s+1)); else commands.append(new String(s+1)); return; } if (flags & EXPAND_VARIABLES) { s = expandVariables(s); flags = flags & ~EXPAND_VARIABLES; } if (flags & EXPAND_ALIASES) expandAliases(s, flags); else if (flags & EXPAND_SPEEDWALK) expandSpeedwalk(s, flags); else if (flags & EXPAND_SEMICOLON) expandSemicolon(s, flags); else if (s[0] == commandCharacter && isdigit(s[1])) { char count[MAX_MUD_BUF]; s = one_argument(s+1, count, false); int n = atoi(count); if (n > 0 && n < 100) { while(n-- > 0) add(s, EXPAND_ALL); } else status->setf("Repeat count must be between 0 and 100"); return; } else { if (back) commands.insert(new String(s)); else commands.append(new String(s)); }}void Interpreter::expandSemicolon(const char *s, int flags) { if ((strchr(s, ';'))) { char buf[strlen(s)+1]; char *out; bool got_semicolon = false; for (;*s;) { for (out = buf; *s; s++ ) { if (*s == '\\' && s[1] == ';') { // escaped semicolon *out++ = ';'; s++; } else if (*s == ';') { got_semicolon = true; break; } else *out++ = *s; } // Deleting spaces just before the ; while (out > buf && isspace(out[-1])) out--; *out = NUL; // Skip the semicolon, if there was one if (*s == ';') s++; // Deleting space just after the ; if appropriate while(*s && isspace(*s)) s++; if (got_semicolon) add(buf, EXPAND_ALL); // unconditionally expand everything so aliases are expanded with their contents else // escaped semicolons only add(buf, flags & ~ EXPAND_SEMICOLON); } } else add(s, flags & ~EXPAND_SEMICOLON);}// ought to move some of this stuff to separate functions, this function is growing LARGEvoid Interpreter::expandAliases(const char* s, int flags) { // Isolate alias name if (!s[0]) // special case, unfortunately add("", EXPAND_NONE); else { int count; char name[MAX_ALIAS_BUFFER]; char buf[MAX_MUD_BUF]; if (embed_interp->run_quietly("sys/command", s, buf)) { if (!buf[0]) // command cancelled return; s = buf; } // Allow e.g. @ or % to be valid alias names without requiring a space // afterwards if (!isalpha(s[0])) { count = 1; name[0] = s[0]; name[1] = NUL; count = 1; } else sscanf(s, "%s %n", name, &count); // skip spaces.. will this break anything? // Easy way to add a command if (embed_interp->run_quietly(Sprintf("cmd_%s", name),s+count, buf, true)) return; // command executed, don't send this anywhere Alias *a = NULL; if (!aliases_disabled) a = currentSession ? currentSession->mud.findAlias(name) : globalMUD.findAlias(name); if (a) { char out_buf[MAX_MUD_BUF]; a->expand(s+count, out_buf); add(out_buf, EXPAND_ALL); // Expand everything again } else // no expansion ocurred add(s, flags & ~ EXPAND_ALIASES); }}// Execute a mcl Commandvoid Interpreter::mclCommand (const char *s){ char cmd[256]; s = one_argument(s, cmd, true); if (!strcmp(cmd, "quit")) mclFinished = true; else if (!strcmp(cmd, "echo")) { output->printf("%s\n", s); } else if (!strcmp(cmd, "status")) { status->setf("%s", s); } else if (!strcmp(cmd, "bell")) { screen->flash(); } else if (!strcmp(cmd, "exec")) { int w = 80, h=10, x=0, y=3, t=10; int c; String res; OptionParser o(s, "w:h:x:y:t:"); while((c = o.nextOption(res))) switch (c) { case 'w': w = atoi(res); break; case 'h': h = atoi(res); break; case 'x': x = atoi(res); break; case 'y': y = atoi(res); break; case 't': t = atoi(res); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -