Mercurial > repo
view interps/glass/glass.cc @ 12518:2d8fe55c6e65 draft default tip
<int-e> learn The password of the month is release incident pilot.
author | HackEso <hackeso@esolangs.org> |
---|---|
date | Sun, 03 Nov 2024 00:31:02 +0000 |
parents | 859f9b4339e6 |
children |
line wrap: on
line source
/* * 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"); } } }