Mercurial > repo
diff interps/glass/glass.cc @ 996:859f9b4339e6
<Gregor> tar xf egobot.tar.xz
author | HackBot |
---|---|
date | Sun, 09 Dec 2012 19:30:08 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/interps/glass/glass.cc Sun Dec 09 19:30:08 2012 +0000 @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2005 Gregor Richards + * + * This file is part of Glass. + * + * Glass 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; either version 2 of the License, or + * (at your option) any later version. + * + * Glass 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Glass; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +using namespace std; + +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "builtins.h" +#include "func.h" +#include "glass.h" +#include "klass.h" +#include "klassi.h" +#include "parseq.h" +#include "variable.h" + +map<string,Variable *> globalVars; + +vector<KlassI *> globalInstant; + +deque<Variable *> mainStack; + +#ifdef IRC +string IRC_o, chan; +unsigned int progTimer; +#define MAXTIME 10000000 +void keepalivePing(int ignore); +#define PINGRATE (60*5) +#endif + +bool parseI(char *inp, int &i, ParseQ *pq); +void stringParse(string &toparse); +void cacheIt(const char *cachef); +void runIt(); +void clearM(); + +int main(int argc, char **argv, char **envp) +{ + char *input, *bins; + FILE *fin; + int fini; + struct stat sbuf; + int i = 0, j; + ParseQ *master; + string eval; + map<string,Variable *>::iterator svari; + + // first read in builtins + bins = strdup(builtinDefinitions); + if (bins) { + master = new ParseQ(); + i = 0; + while (parseI(bins, i, master)); + master->parseKlasses(); + delete master; + free(bins); + } + +#ifndef IRC + if (argc < 2) { + fprintf(stderr, "Use: glass [cache] <input>\n"); + return 1; + } + + // non-IRC gets environment variables + for (i = 0; envp[i]; i++) { + for (j = 0; envp[i][j] && envp[i][j] != '='; j++); + if (!envp[i][j]) continue; + eval = envp[i]; + globalVars["Env" + eval.substr(0, j)] = new Variable(VAR_STRING, eval.substr(j + 1)); + } + +#else + if (argc < 5) { + cout << "Use: glass <cachefile> <username> <master> <channel>" << endl; + return 1; + } +#endif + + for (j = 1; argv[j]; j++) { + // open the file ... + fin = fopen(argv[j], "r"); + if (!fin) continue; + + // get the size ... + fini = fileno(fin); + if (fstat(fini, &sbuf) == -1) continue; + + // allocate + input = (char *) malloc(sbuf.st_size + 2); + input[sbuf.st_size] = '\0'; + + // read + if (fread(input, 1, sbuf.st_size, fin) < 0) continue; + + // parse + master = new ParseQ(); + i = 0; + while (parseI(input, i, master)); + master->parseKlasses(); + delete master; + + // free + free(input); + + // if this is our cache file, ignore the M + if (j == 1 && argv[2]) { + clearM(); + } + } + +#ifndef IRC + // cache if necessary + if (argv[2]) cacheIt(argv[1]); + + // if we're not in IRC mode, this is the file to run + runIt(); +#else + // IRC + + char line[32257]; + string sline; + line[32256] = '\0'; + int mode = 0; + chan = argv[4]; + + // schedule keepalive pings + signal(14, keepalivePing); + alarm(PINGRATE); + + // fix our cache (which will probably have a faulty M in it) + clearM(); + + while (true) { + line[0] = '\0'; + while (!fgets(line, 1024, stdin)) { + // we can't use sleep() directly due to alarm() + int pid = fork(); + if (pid == 0) { + sleep(1); + exit(0); + } else if (pid > 0) { + wait(NULL); + } + } + sline = line; + cout << sline << endl; + + if (sline.substr(0, 5) == "PING ") { + cerr << "PONG :localhost\r\n"; + continue; + } + + switch (mode) { + case 0: + // tell it our nick + cerr << "USER " << argv[2] << " localhost localhost :GlassBot\r\n"; + cerr << "NICK " << argv[2] << "\r\n"; + mode++; + break; + + case 1: + // join the channel + cerr << "JOIN #" << chan << "\r\n"; + mode++; + break; + + case 2: + // the real heavy lifting + if (sline[0] == ':') { + unsigned int ui; + unsigned int gloc = 0; + string sender, retsend; + + // parse for the ! + for (ui = 0; ui < sline.length() && sline[ui] != '!' && sline[ui] != ' '; ui++); + if (ui == sline.length()) continue; + // get the sender + sender = sline.substr(1, ui - 1); + + // parse for the space + for (; ui < sline.length() && sline[ui] != ' '; ui++); + if (ui == sline.length()) continue; + sline = sline.substr(ui + 1); + + // it should be "PRIVMSG <something> :G!..." + if (sline.substr(0, 8) == "PRIVMSG " && + (gloc = sline.find(":G!", 0)) != string::npos) { + // where should we return it to? + retsend = sline.substr(8, gloc - 9); + if (retsend[0] != '#') { + // send it to the sender + retsend = sender; + } + + // we only need what's after G! + sline = sline.substr(gloc + 3); + strcpy(line, sline.c_str()); + + // maybe it's a URL + if (sline[0] == 'U') { + sline = sline.substr(1); + strcpy(line, sline.c_str()); + + // make sure the URL is good + for (ui = 0; line[ui]; ui++) { + switch (line[ui]) { + case '\r': + case '\n': + case '?': + line[ui] = '\0'; + break; + } + } + if (strncmp(line, "http://", 7)) { + line[0] = '\0'; + } + + // fork off a pid to do lynx --dump + int urli[2], pid; + pipe(urli); + pid = fork(); + if (pid == 0) { + dup2(urli[1], 1); + dup2(urli[1], 2); + execlp("lynx", "lynx", "--dump", line, + "-connect_timeout", "15", NULL); + exit(0); + } else if (pid >= 0) { + read(urli[0], line, 32256); + wait(NULL); + } + close(urli[0]); + close(urli[1]); + } + + // we now have our input, parse! + master = new ParseQ(); + i = 0; + while (parseI(line, i, master)); + master->parseKlasses(); + delete master; + + // reset any destroyed builtins + bins = strdup(builtinDefinitions); + if (bins) { + master = new ParseQ(); + i = 0; + while (parseI(bins, i, master)); + master->parseKlasses(); + delete master; + free(bins); + } + + cacheIt(argv[1]); + runIt(); + + // display the output + if (IRC_o != "") { + // get rid of possible injections + for (ui = 0; ui < IRC_o.length(); ui++) { + switch (IRC_o[ui]) { + case '\n': + case '\r': + IRC_o[ui] = ' '; + break; + } + } + alarm(0); + // allow our master to do raw sending + if (sender == argv[3] && IRC_o[0] == '^') { + cerr << IRC_o.substr(1) << "\r\n"; + } else { + // max of 400 chars (about) + if (IRC_o.length() > 400) { + IRC_o = IRC_o.substr(0, 393) + " Flood!"; + } + cerr << "PRIVMSG " << retsend << " :" << IRC_o << "\r\n"; + } + alarm(PINGRATE); + } + + // then garbage collection + for (ui = 0; ui < globalInstant.size(); ui++) { + delete globalInstant[ui]; + } + globalInstant.clear(); + for (ui = 0; ui < mainStack.size(); ui++) { + delete mainStack[ui]; + } + mainStack.clear(); + clearM(); + /* we have to keep a vector of strings to properly erase globalVars, + * otherwise we'll end up messing up the iterator mid-loop */ + vector<string> todel; + for (svari = globalVars.begin(); svari != globalVars.end(); svari++) { + if (svari->second->type != VAR_KLASS) { + // any user global variables are deleted + delete svari->second; + todel.push_back(svari->first); + } + } + for (ui = 0; ui < todel.size(); ui++) { + globalVars.erase(todel[ui]); + } + todel.clear(); + } + } + break; + } + } +#endif + + return 0; +} + +bool parseI(char *inp, int &i, ParseQ *pq) +{ + int j; + + // this function stops iterating at inp[i] == '\0' + if (!inp[i]) return false; + + if (inp[i] >= 'A' && inp[i] <= 'Z') { + pq->add(new ParseQElement(PQT_GLOBAL, inp[i])); + } else if (inp[i] >= 'a' && inp[i] <= 'z') { + pq->add(new ParseQElement(PQT_CLASSWIDE, inp[i])); + } else if (inp[i] >= '0' && inp[i] <= '9') { + pq->add(new ParseQElement(PQT_STACK, inp[i])); + } else if (inp[i] == '(') { + i++; + for (j = i; inp[j] && inp[j] != ')'; j++); + if (!inp[j]) { + // ERROR + return false; + } + + // now we're at the ) + inp[j] = '\0'; + // now some sub-parsing + if (inp[i] >= 'A' && inp[i] <= 'Z') { + pq->add(new ParseQElement(PQT_GLOBAL, inp + i)); + } else if (inp[i] >= 'a' && inp[i] <= 'z') { + pq->add(new ParseQElement(PQT_CLASSWIDE, inp + i)); + } else if (inp[i] >= '0' && inp[i] <= '9') { + pq->add(new ParseQElement(PQT_STACK, inp + i)); + } else if (inp[i] == '_') { + pq->add(new ParseQElement(PQT_LOCAL, inp + i)); + } + i = j; + } else if (inp[i] == '"') { + // push a string + i++; + for (j = i; inp[j] && inp[j] != '"'; j++); + if (!inp[j]) { + // ERROR + return false; + } + + // now we're on the ending " + inp[j] = '\0'; + string tomk = inp + i; + + stringParse(tomk); + + pq->add(new ParseQElement(PQT_STRING, tomk)); + i = j; + } else if (inp[i] == '<') { + // push a number + i++; + for (j = i; inp[j] && inp[j] != '>'; j++); + if (!inp[j]) { + // ERROR + return false; + } + + // now we're on the > + inp[j] = '\0'; + pq->add(new ParseQElement(PQT_NUMBER, inp + i)); + i = j; + } else if (inp[i] == '~') { + // special, builtin + i++; + for (j = i; inp[j] && inp[j] != '~'; j++); + if (!inp[j]) { + // ERROR + return false; + } + + // now we're on the ending " + inp[j] = '\0'; + pq->add(new ParseQElement(PQT_BUILTIN, inp + i)); + i = j; + } else if (inp[i] == '\'') { + // comment + i++; + for (; inp[i] && inp[i] != '\''; i++); + if (!inp[i]) { + // ERROR + return false; + } + } else if (inp[i] != ' ' && inp[i] != '\t' && + inp[i] != '\n' && inp[i] != '\r') { + // the rest are all commands (or ignorable non-commands) + pq->add(new ParseQElement(PQT_COMMAND, inp[i])); + } + i++; + return true; +} + +void stringParse(string &toparse) +{ + unsigned int i; + + for (i = 0; i < toparse.length(); i++) { + if (toparse[i] == '\\') { + switch (toparse[i+1]) { + case 'n': + toparse.replace(i, 2, string("\n")); + break; + + default: + toparse.replace(i, 2, toparse.substr(i + 1, 1)); + } + } + } +} + +Variable *getVar(KlassI *klass, map<string,Variable *> *locals, string name) +{ + // what type of var? + char fchar = name[0]; + if (fchar >= 'A' && fchar <= 'Z') { + // global + if (globalVars.find(name) == globalVars.end()) { + globalVars[name] = new Variable(); + } + return globalVars[name]; + } else if (fchar >= 'a' && fchar <= 'z') { + // classwide + if (klass->vars.find(name) == klass->vars.end()) { + klass->vars[name] = new Variable(); + // check if it's a function that hasn't been allocated in the klassi due to lazy allocation + if (klass->of->functions.find(name) != klass->of->functions.end()) { + klass->vars[name]->type = VAR_FUNC; + klass->vars[name]->fval = klass->of->functions[name]; + } + } + return klass->vars[name]; + } else if (fchar == '_') { + // local + if (locals->find(name) == locals->end()) { + (*locals)[name] = new Variable(); + } + return (*locals)[name]; + } else { + // ERROR + exit(2); + } +} + +#ifdef IRC +void keepalivePing(int ignore) +{ + cerr << "PING :localhost\r\n"; + alarm(PINGRATE); +} +#endif + + +void cacheIt(const char *cachef) +{ + FILE *fin; + map<string,Variable *>::iterator svari; + + // cache it + fin = fopen(cachef, "w"); + if (fin) { + Klass *curklass; + map<string,Func *>::iterator fvari; + for (svari = globalVars.begin(); svari != globalVars.end(); svari++) { + // for each klass ... + if (svari->second->type == VAR_KLASS) { + // write the name ... + fputs("{(", fin); + fputs(svari->first.c_str(), fin); + fputs(")", fin); + curklass = svari->second->kval; + // for each function ... + for (fvari = curklass->functions.begin(); fvari != + curklass->functions.end(); fvari++) { + string cont; + // write the name ... + fputs("[(", fin); + fputs(fvari->second->name.c_str(), fin); + fputs(")", fin); + // and the content + cont = fvari->second->contents->dump(); + fputs(cont.c_str(), fin); + // and close it + fputs("]", fin); + } + // close it + fputs("}", fin); + } + } + fclose(fin); + } +} + +void runIt() +{ + // now run M.m + Variable *Mvar; + Klass *M; + KlassI *MI; + if (globalVars.find("M") == globalVars.end()) { +#ifndef IRC + cout << "OK" << endl; + return; +#else + IRC_o = ""; + cerr << "PRIVMSG #" << chan << " :OK\r\n" << endl; + return; +#endif + } + Mvar = globalVars["M"]; + + if (Mvar->type != VAR_KLASS) { +#ifndef IRC + cout << "OK" << endl; + return; +#else + IRC_o = ""; + cerr << "PRIVMSG #" << chan << " :OK\r\n" << endl; + return; +#endif + } + + M = Mvar->kval; + MI = new KlassI(); + globalVars["MI"] = new Variable(VAR_KLASSI, MI); + MI->of = M; + + // now make sure M.m exists + if (M->functions.find("m") == M->functions.end()) { + cout << "NO M.m!" << endl; + return; + } + + // and run it +#ifdef IRC + IRC_o = ""; + progTimer = MAXTIME; +#endif + M->functions["m"]->contents->runFunc(MI, M->functions["m"]); +} + +void clearM() +{ + if (globalVars.find("MI") != globalVars.end()) { + if (globalVars["MI"]->type == VAR_KLASSI) { + delete globalVars["MI"]->kival; + delete globalVars["MI"]; + globalVars.erase("MI"); + } + } + + if (globalVars.find("M") != globalVars.end()) { + if (globalVars["M"]->type == VAR_KLASS) { + delete globalVars["M"]->kval; + delete globalVars["M"]; + globalVars.erase("M"); + } + } +}