view interps/glass/glass.cc @ 9554:23f43464694e

<Zarutian> le/rn Frams\xc3\xb3knarflokkurinn/A, now defunct, political party in Iceland. Like its sister party Sj\xc3\xa1lfst\xc3\xa6\xc3\xb0isflokkurinn it is named by the antonym of what it is. (The name means the Progressive Party but they have nearly always been highly regressive). Think dumb Hill-Billies in ill fitting suits and you get their constiuents.
author HackBot
date Sun, 30 Oct 2016 14:33:24 +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");
        }
    }
}