Mercurial > repo
comparison 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 |
comparison
equal
deleted
inserted
replaced
995:6883f5911eb7 | 996:859f9b4339e6 |
---|---|
1 /* | |
2 * Copyright (c) 2005 Gregor Richards | |
3 * | |
4 * This file is part of Glass. | |
5 * | |
6 * Glass is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * Glass is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with Glass; if not, write to the Free Software | |
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 */ | |
20 | |
21 #include <iostream> | |
22 using namespace std; | |
23 | |
24 #include <signal.h> | |
25 #include <stdio.h> | |
26 #include <stdlib.h> | |
27 #include <string.h> | |
28 #include <sys/stat.h> | |
29 #include <sys/types.h> | |
30 #include <sys/wait.h> | |
31 #include <unistd.h> | |
32 | |
33 #include "builtins.h" | |
34 #include "func.h" | |
35 #include "glass.h" | |
36 #include "klass.h" | |
37 #include "klassi.h" | |
38 #include "parseq.h" | |
39 #include "variable.h" | |
40 | |
41 map<string,Variable *> globalVars; | |
42 | |
43 vector<KlassI *> globalInstant; | |
44 | |
45 deque<Variable *> mainStack; | |
46 | |
47 #ifdef IRC | |
48 string IRC_o, chan; | |
49 unsigned int progTimer; | |
50 #define MAXTIME 10000000 | |
51 void keepalivePing(int ignore); | |
52 #define PINGRATE (60*5) | |
53 #endif | |
54 | |
55 bool parseI(char *inp, int &i, ParseQ *pq); | |
56 void stringParse(string &toparse); | |
57 void cacheIt(const char *cachef); | |
58 void runIt(); | |
59 void clearM(); | |
60 | |
61 int main(int argc, char **argv, char **envp) | |
62 { | |
63 char *input, *bins; | |
64 FILE *fin; | |
65 int fini; | |
66 struct stat sbuf; | |
67 int i = 0, j; | |
68 ParseQ *master; | |
69 string eval; | |
70 map<string,Variable *>::iterator svari; | |
71 | |
72 // first read in builtins | |
73 bins = strdup(builtinDefinitions); | |
74 if (bins) { | |
75 master = new ParseQ(); | |
76 i = 0; | |
77 while (parseI(bins, i, master)); | |
78 master->parseKlasses(); | |
79 delete master; | |
80 free(bins); | |
81 } | |
82 | |
83 #ifndef IRC | |
84 if (argc < 2) { | |
85 fprintf(stderr, "Use: glass [cache] <input>\n"); | |
86 return 1; | |
87 } | |
88 | |
89 // non-IRC gets environment variables | |
90 for (i = 0; envp[i]; i++) { | |
91 for (j = 0; envp[i][j] && envp[i][j] != '='; j++); | |
92 if (!envp[i][j]) continue; | |
93 eval = envp[i]; | |
94 globalVars["Env" + eval.substr(0, j)] = new Variable(VAR_STRING, eval.substr(j + 1)); | |
95 } | |
96 | |
97 #else | |
98 if (argc < 5) { | |
99 cout << "Use: glass <cachefile> <username> <master> <channel>" << endl; | |
100 return 1; | |
101 } | |
102 #endif | |
103 | |
104 for (j = 1; argv[j]; j++) { | |
105 // open the file ... | |
106 fin = fopen(argv[j], "r"); | |
107 if (!fin) continue; | |
108 | |
109 // get the size ... | |
110 fini = fileno(fin); | |
111 if (fstat(fini, &sbuf) == -1) continue; | |
112 | |
113 // allocate | |
114 input = (char *) malloc(sbuf.st_size + 2); | |
115 input[sbuf.st_size] = '\0'; | |
116 | |
117 // read | |
118 if (fread(input, 1, sbuf.st_size, fin) < 0) continue; | |
119 | |
120 // parse | |
121 master = new ParseQ(); | |
122 i = 0; | |
123 while (parseI(input, i, master)); | |
124 master->parseKlasses(); | |
125 delete master; | |
126 | |
127 // free | |
128 free(input); | |
129 | |
130 // if this is our cache file, ignore the M | |
131 if (j == 1 && argv[2]) { | |
132 clearM(); | |
133 } | |
134 } | |
135 | |
136 #ifndef IRC | |
137 // cache if necessary | |
138 if (argv[2]) cacheIt(argv[1]); | |
139 | |
140 // if we're not in IRC mode, this is the file to run | |
141 runIt(); | |
142 #else | |
143 // IRC | |
144 | |
145 char line[32257]; | |
146 string sline; | |
147 line[32256] = '\0'; | |
148 int mode = 0; | |
149 chan = argv[4]; | |
150 | |
151 // schedule keepalive pings | |
152 signal(14, keepalivePing); | |
153 alarm(PINGRATE); | |
154 | |
155 // fix our cache (which will probably have a faulty M in it) | |
156 clearM(); | |
157 | |
158 while (true) { | |
159 line[0] = '\0'; | |
160 while (!fgets(line, 1024, stdin)) { | |
161 // we can't use sleep() directly due to alarm() | |
162 int pid = fork(); | |
163 if (pid == 0) { | |
164 sleep(1); | |
165 exit(0); | |
166 } else if (pid > 0) { | |
167 wait(NULL); | |
168 } | |
169 } | |
170 sline = line; | |
171 cout << sline << endl; | |
172 | |
173 if (sline.substr(0, 5) == "PING ") { | |
174 cerr << "PONG :localhost\r\n"; | |
175 continue; | |
176 } | |
177 | |
178 switch (mode) { | |
179 case 0: | |
180 // tell it our nick | |
181 cerr << "USER " << argv[2] << " localhost localhost :GlassBot\r\n"; | |
182 cerr << "NICK " << argv[2] << "\r\n"; | |
183 mode++; | |
184 break; | |
185 | |
186 case 1: | |
187 // join the channel | |
188 cerr << "JOIN #" << chan << "\r\n"; | |
189 mode++; | |
190 break; | |
191 | |
192 case 2: | |
193 // the real heavy lifting | |
194 if (sline[0] == ':') { | |
195 unsigned int ui; | |
196 unsigned int gloc = 0; | |
197 string sender, retsend; | |
198 | |
199 // parse for the ! | |
200 for (ui = 0; ui < sline.length() && sline[ui] != '!' && sline[ui] != ' '; ui++); | |
201 if (ui == sline.length()) continue; | |
202 // get the sender | |
203 sender = sline.substr(1, ui - 1); | |
204 | |
205 // parse for the space | |
206 for (; ui < sline.length() && sline[ui] != ' '; ui++); | |
207 if (ui == sline.length()) continue; | |
208 sline = sline.substr(ui + 1); | |
209 | |
210 // it should be "PRIVMSG <something> :G!..." | |
211 if (sline.substr(0, 8) == "PRIVMSG " && | |
212 (gloc = sline.find(":G!", 0)) != string::npos) { | |
213 // where should we return it to? | |
214 retsend = sline.substr(8, gloc - 9); | |
215 if (retsend[0] != '#') { | |
216 // send it to the sender | |
217 retsend = sender; | |
218 } | |
219 | |
220 // we only need what's after G! | |
221 sline = sline.substr(gloc + 3); | |
222 strcpy(line, sline.c_str()); | |
223 | |
224 // maybe it's a URL | |
225 if (sline[0] == 'U') { | |
226 sline = sline.substr(1); | |
227 strcpy(line, sline.c_str()); | |
228 | |
229 // make sure the URL is good | |
230 for (ui = 0; line[ui]; ui++) { | |
231 switch (line[ui]) { | |
232 case '\r': | |
233 case '\n': | |
234 case '?': | |
235 line[ui] = '\0'; | |
236 break; | |
237 } | |
238 } | |
239 if (strncmp(line, "http://", 7)) { | |
240 line[0] = '\0'; | |
241 } | |
242 | |
243 // fork off a pid to do lynx --dump | |
244 int urli[2], pid; | |
245 pipe(urli); | |
246 pid = fork(); | |
247 if (pid == 0) { | |
248 dup2(urli[1], 1); | |
249 dup2(urli[1], 2); | |
250 execlp("lynx", "lynx", "--dump", line, | |
251 "-connect_timeout", "15", NULL); | |
252 exit(0); | |
253 } else if (pid >= 0) { | |
254 read(urli[0], line, 32256); | |
255 wait(NULL); | |
256 } | |
257 close(urli[0]); | |
258 close(urli[1]); | |
259 } | |
260 | |
261 // we now have our input, parse! | |
262 master = new ParseQ(); | |
263 i = 0; | |
264 while (parseI(line, i, master)); | |
265 master->parseKlasses(); | |
266 delete master; | |
267 | |
268 // reset any destroyed builtins | |
269 bins = strdup(builtinDefinitions); | |
270 if (bins) { | |
271 master = new ParseQ(); | |
272 i = 0; | |
273 while (parseI(bins, i, master)); | |
274 master->parseKlasses(); | |
275 delete master; | |
276 free(bins); | |
277 } | |
278 | |
279 cacheIt(argv[1]); | |
280 runIt(); | |
281 | |
282 // display the output | |
283 if (IRC_o != "") { | |
284 // get rid of possible injections | |
285 for (ui = 0; ui < IRC_o.length(); ui++) { | |
286 switch (IRC_o[ui]) { | |
287 case '\n': | |
288 case '\r': | |
289 IRC_o[ui] = ' '; | |
290 break; | |
291 } | |
292 } | |
293 alarm(0); | |
294 // allow our master to do raw sending | |
295 if (sender == argv[3] && IRC_o[0] == '^') { | |
296 cerr << IRC_o.substr(1) << "\r\n"; | |
297 } else { | |
298 // max of 400 chars (about) | |
299 if (IRC_o.length() > 400) { | |
300 IRC_o = IRC_o.substr(0, 393) + " Flood!"; | |
301 } | |
302 cerr << "PRIVMSG " << retsend << " :" << IRC_o << "\r\n"; | |
303 } | |
304 alarm(PINGRATE); | |
305 } | |
306 | |
307 // then garbage collection | |
308 for (ui = 0; ui < globalInstant.size(); ui++) { | |
309 delete globalInstant[ui]; | |
310 } | |
311 globalInstant.clear(); | |
312 for (ui = 0; ui < mainStack.size(); ui++) { | |
313 delete mainStack[ui]; | |
314 } | |
315 mainStack.clear(); | |
316 clearM(); | |
317 /* we have to keep a vector of strings to properly erase globalVars, | |
318 * otherwise we'll end up messing up the iterator mid-loop */ | |
319 vector<string> todel; | |
320 for (svari = globalVars.begin(); svari != globalVars.end(); svari++) { | |
321 if (svari->second->type != VAR_KLASS) { | |
322 // any user global variables are deleted | |
323 delete svari->second; | |
324 todel.push_back(svari->first); | |
325 } | |
326 } | |
327 for (ui = 0; ui < todel.size(); ui++) { | |
328 globalVars.erase(todel[ui]); | |
329 } | |
330 todel.clear(); | |
331 } | |
332 } | |
333 break; | |
334 } | |
335 } | |
336 #endif | |
337 | |
338 return 0; | |
339 } | |
340 | |
341 bool parseI(char *inp, int &i, ParseQ *pq) | |
342 { | |
343 int j; | |
344 | |
345 // this function stops iterating at inp[i] == '\0' | |
346 if (!inp[i]) return false; | |
347 | |
348 if (inp[i] >= 'A' && inp[i] <= 'Z') { | |
349 pq->add(new ParseQElement(PQT_GLOBAL, inp[i])); | |
350 } else if (inp[i] >= 'a' && inp[i] <= 'z') { | |
351 pq->add(new ParseQElement(PQT_CLASSWIDE, inp[i])); | |
352 } else if (inp[i] >= '0' && inp[i] <= '9') { | |
353 pq->add(new ParseQElement(PQT_STACK, inp[i])); | |
354 } else if (inp[i] == '(') { | |
355 i++; | |
356 for (j = i; inp[j] && inp[j] != ')'; j++); | |
357 if (!inp[j]) { | |
358 // ERROR | |
359 return false; | |
360 } | |
361 | |
362 // now we're at the ) | |
363 inp[j] = '\0'; | |
364 // now some sub-parsing | |
365 if (inp[i] >= 'A' && inp[i] <= 'Z') { | |
366 pq->add(new ParseQElement(PQT_GLOBAL, inp + i)); | |
367 } else if (inp[i] >= 'a' && inp[i] <= 'z') { | |
368 pq->add(new ParseQElement(PQT_CLASSWIDE, inp + i)); | |
369 } else if (inp[i] >= '0' && inp[i] <= '9') { | |
370 pq->add(new ParseQElement(PQT_STACK, inp + i)); | |
371 } else if (inp[i] == '_') { | |
372 pq->add(new ParseQElement(PQT_LOCAL, inp + i)); | |
373 } | |
374 i = j; | |
375 } else if (inp[i] == '"') { | |
376 // push a string | |
377 i++; | |
378 for (j = i; inp[j] && inp[j] != '"'; j++); | |
379 if (!inp[j]) { | |
380 // ERROR | |
381 return false; | |
382 } | |
383 | |
384 // now we're on the ending " | |
385 inp[j] = '\0'; | |
386 string tomk = inp + i; | |
387 | |
388 stringParse(tomk); | |
389 | |
390 pq->add(new ParseQElement(PQT_STRING, tomk)); | |
391 i = j; | |
392 } else if (inp[i] == '<') { | |
393 // push a number | |
394 i++; | |
395 for (j = i; inp[j] && inp[j] != '>'; j++); | |
396 if (!inp[j]) { | |
397 // ERROR | |
398 return false; | |
399 } | |
400 | |
401 // now we're on the > | |
402 inp[j] = '\0'; | |
403 pq->add(new ParseQElement(PQT_NUMBER, inp + i)); | |
404 i = j; | |
405 } else if (inp[i] == '~') { | |
406 // special, builtin | |
407 i++; | |
408 for (j = i; inp[j] && inp[j] != '~'; j++); | |
409 if (!inp[j]) { | |
410 // ERROR | |
411 return false; | |
412 } | |
413 | |
414 // now we're on the ending " | |
415 inp[j] = '\0'; | |
416 pq->add(new ParseQElement(PQT_BUILTIN, inp + i)); | |
417 i = j; | |
418 } else if (inp[i] == '\'') { | |
419 // comment | |
420 i++; | |
421 for (; inp[i] && inp[i] != '\''; i++); | |
422 if (!inp[i]) { | |
423 // ERROR | |
424 return false; | |
425 } | |
426 } else if (inp[i] != ' ' && inp[i] != '\t' && | |
427 inp[i] != '\n' && inp[i] != '\r') { | |
428 // the rest are all commands (or ignorable non-commands) | |
429 pq->add(new ParseQElement(PQT_COMMAND, inp[i])); | |
430 } | |
431 i++; | |
432 return true; | |
433 } | |
434 | |
435 void stringParse(string &toparse) | |
436 { | |
437 unsigned int i; | |
438 | |
439 for (i = 0; i < toparse.length(); i++) { | |
440 if (toparse[i] == '\\') { | |
441 switch (toparse[i+1]) { | |
442 case 'n': | |
443 toparse.replace(i, 2, string("\n")); | |
444 break; | |
445 | |
446 default: | |
447 toparse.replace(i, 2, toparse.substr(i + 1, 1)); | |
448 } | |
449 } | |
450 } | |
451 } | |
452 | |
453 Variable *getVar(KlassI *klass, map<string,Variable *> *locals, string name) | |
454 { | |
455 // what type of var? | |
456 char fchar = name[0]; | |
457 if (fchar >= 'A' && fchar <= 'Z') { | |
458 // global | |
459 if (globalVars.find(name) == globalVars.end()) { | |
460 globalVars[name] = new Variable(); | |
461 } | |
462 return globalVars[name]; | |
463 } else if (fchar >= 'a' && fchar <= 'z') { | |
464 // classwide | |
465 if (klass->vars.find(name) == klass->vars.end()) { | |
466 klass->vars[name] = new Variable(); | |
467 // check if it's a function that hasn't been allocated in the klassi due to lazy allocation | |
468 if (klass->of->functions.find(name) != klass->of->functions.end()) { | |
469 klass->vars[name]->type = VAR_FUNC; | |
470 klass->vars[name]->fval = klass->of->functions[name]; | |
471 } | |
472 } | |
473 return klass->vars[name]; | |
474 } else if (fchar == '_') { | |
475 // local | |
476 if (locals->find(name) == locals->end()) { | |
477 (*locals)[name] = new Variable(); | |
478 } | |
479 return (*locals)[name]; | |
480 } else { | |
481 // ERROR | |
482 exit(2); | |
483 } | |
484 } | |
485 | |
486 #ifdef IRC | |
487 void keepalivePing(int ignore) | |
488 { | |
489 cerr << "PING :localhost\r\n"; | |
490 alarm(PINGRATE); | |
491 } | |
492 #endif | |
493 | |
494 | |
495 void cacheIt(const char *cachef) | |
496 { | |
497 FILE *fin; | |
498 map<string,Variable *>::iterator svari; | |
499 | |
500 // cache it | |
501 fin = fopen(cachef, "w"); | |
502 if (fin) { | |
503 Klass *curklass; | |
504 map<string,Func *>::iterator fvari; | |
505 for (svari = globalVars.begin(); svari != globalVars.end(); svari++) { | |
506 // for each klass ... | |
507 if (svari->second->type == VAR_KLASS) { | |
508 // write the name ... | |
509 fputs("{(", fin); | |
510 fputs(svari->first.c_str(), fin); | |
511 fputs(")", fin); | |
512 curklass = svari->second->kval; | |
513 // for each function ... | |
514 for (fvari = curklass->functions.begin(); fvari != | |
515 curklass->functions.end(); fvari++) { | |
516 string cont; | |
517 // write the name ... | |
518 fputs("[(", fin); | |
519 fputs(fvari->second->name.c_str(), fin); | |
520 fputs(")", fin); | |
521 // and the content | |
522 cont = fvari->second->contents->dump(); | |
523 fputs(cont.c_str(), fin); | |
524 // and close it | |
525 fputs("]", fin); | |
526 } | |
527 // close it | |
528 fputs("}", fin); | |
529 } | |
530 } | |
531 fclose(fin); | |
532 } | |
533 } | |
534 | |
535 void runIt() | |
536 { | |
537 // now run M.m | |
538 Variable *Mvar; | |
539 Klass *M; | |
540 KlassI *MI; | |
541 if (globalVars.find("M") == globalVars.end()) { | |
542 #ifndef IRC | |
543 cout << "OK" << endl; | |
544 return; | |
545 #else | |
546 IRC_o = ""; | |
547 cerr << "PRIVMSG #" << chan << " :OK\r\n" << endl; | |
548 return; | |
549 #endif | |
550 } | |
551 Mvar = globalVars["M"]; | |
552 | |
553 if (Mvar->type != VAR_KLASS) { | |
554 #ifndef IRC | |
555 cout << "OK" << endl; | |
556 return; | |
557 #else | |
558 IRC_o = ""; | |
559 cerr << "PRIVMSG #" << chan << " :OK\r\n" << endl; | |
560 return; | |
561 #endif | |
562 } | |
563 | |
564 M = Mvar->kval; | |
565 MI = new KlassI(); | |
566 globalVars["MI"] = new Variable(VAR_KLASSI, MI); | |
567 MI->of = M; | |
568 | |
569 // now make sure M.m exists | |
570 if (M->functions.find("m") == M->functions.end()) { | |
571 cout << "NO M.m!" << endl; | |
572 return; | |
573 } | |
574 | |
575 // and run it | |
576 #ifdef IRC | |
577 IRC_o = ""; | |
578 progTimer = MAXTIME; | |
579 #endif | |
580 M->functions["m"]->contents->runFunc(MI, M->functions["m"]); | |
581 } | |
582 | |
583 void clearM() | |
584 { | |
585 if (globalVars.find("MI") != globalVars.end()) { | |
586 if (globalVars["MI"]->type == VAR_KLASSI) { | |
587 delete globalVars["MI"]->kival; | |
588 delete globalVars["MI"]; | |
589 globalVars.erase("MI"); | |
590 } | |
591 } | |
592 | |
593 if (globalVars.find("M") != globalVars.end()) { | |
594 if (globalVars["M"]->type == VAR_KLASS) { | |
595 delete globalVars["M"]->kval; | |
596 delete globalVars["M"]; | |
597 globalVars.erase("M"); | |
598 } | |
599 } | |
600 } |