1 // Copyright (C) 1995-1999 David Sugar, Tycho Softworks.
2 // Copyright (C) 1999-2005 Open Source Telecom Corp.
3 // Copyright (C) 2005-2011 David Sugar, Tycho Softworks.
4 //
5 // This file is part of GNU Bayonne.
6 //
7 // GNU Bayonne is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // GNU Bayonne is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with GNU Bayonne. If not, see <http://www.gnu.org/licenses/>.
19
20 #include <config.h>
21 #include <ucommon/ucommon.h>
22 #include <ucommon/export.h>
24 #include <ctype.h>
25
27 using namespace UCOMMON_NAMESPACE;
28
29 static Script::keyword_t *keywords = NULL;
30
31 size_t Script::paging = 0; // default page size used...
32 unsigned Script::sizing = 64; // default symbol size is 64...
33 unsigned Script::indexing = 77; // hash size of symbol index
34 unsigned Script::stacking = 20; // max stack depth 20...
35 unsigned Script::stepping = 7; // automatic stepping...
36 unsigned Script::decimals = 2; // two decimal places...us default
37
38 static bool ideq(const char *id1, const char *id2)
39 {
40 unsigned count = 0;
41
42 if(!id1)
43 id1 = "";
44
45 if(!id2)
46 id2 = "";
47
48 while(id1[count] && id1[count] != ':') {
49 if(id2[count] != id1[count])
50 return false;
51 ++count;
52 }
53 if(id2[count] && id2[count] != ':')
54 return false;
55
56 return true;
57 }
58 static bool iskeyword(const char *str)
59 {
60 if(!str || !*str)
61 return false;
62
63 if(*str == '_')
64 ++str;
65
66 while(*str) {
67 if(!isalpha(*str) && *str != '.')
68 return false;
69 ++str;
70 }
71 return true;
72 }
73
74 static bool isend(const char *str)
75 {
76 if(!str || !*str)
77 return true;
78
79 if(!strncmp("%%", str, 2))
80 return true;
81
82 if(!strncmp("//", str, 2))
83 return true;
84
85 return false;
86 }
87
88 static bool preparse(char **tokens)
89 {
90 if(!tokens || !*tokens)
91 return true;
92
93 while(*tokens && isspace(**tokens))
94 ++*tokens;
95
96 char *str = *tokens;
97 if(isend(str)) {
98 *str = 0;
99 return true;
100 }
101
102 switch(*str) {
103 case '=':
104 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2]))) {
105 --*tokens;
106 **tokens = '?';
107 return true;
108 }
109 return false;
110 #ifdef HAVE_REGEX_H
111 case '~':
112 if(isspace(str[1])) {
113 --*tokens;
114 **tokens = '?';
115 return true;
116 }
117 return false;
118 #endif
119 case '>':
120 if(eq(str, ">>%", 3)) {
121 --*tokens;
122 --str;
123 str[0] = '$';
124 str[3] = ':';
125 return true;
126 }
127 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2]))) {
128 --*tokens;
129 **tokens = '?';
130 return true;
131 }
132 return false;
133 case '$':
134 if(isspace(str[1])) {
135 --*tokens;
136 **tokens = '?';
137 return true;
138 }
139 break;
140 case '?':
141 if(isspace(str[1])) {
142 --*tokens;
143 **tokens = '?';
144 return true;
145 }
146 return false;
147 case '<':
148 if(eq(str, "<<%", 3)) {
149 --*tokens;
150 --str;
151 str[0] = '$';
152 str[3] = ':';
153 return true;
154 }
155 if(isspace(str[1]) || (str[1] == '=' && isspace(str[2])) || (str[1] == '>' && isspace(str[2]))) {
156 --*tokens;
157 **tokens = '?';
158 return true;
159 }
160 return false;
161 case '!':
162 if(str[1] == '=' && isspace(str[2])) {
163 --*tokens;
164 **tokens = '?';
165 return true;
166 }
167 else if(str[1] == '?' && isspace(str[2])) {
168 --*tokens;
169 **tokens = '?';
170 return true;
171 }
172 else if(str[1] == '$' && isspace(str[2])) {
173 --*tokens;
174 **tokens = '?';
175 return true;
176 }
177 else if(str[1] == '~' && isspace(str[2])) {
178 --*tokens;
179 **tokens = '?';
180 return true;
181 }
182 if(isalnum(str[1]))
183 break;
184 return false;
185 case '&':
186 if(str[1] == '&' && isspace(str[2])) {
187 --*tokens;
188 **tokens = '?';
189 return true;
190 }
191 else if(isalnum(str[1])) {
192 *str = '%';
193 break;
194 }
195 return false;
196 case '|':
197 if(str[1] == '|' && isspace(str[2])) {
198 --*tokens;
199 **tokens = '?';
200 return true;
201 }
202 return false;
203 case '/':
204 case '*':
205 if(isspace(str[1]))
206 break;
207 if(str[1] == '=' && isspace(str[2]))
208 break;
209 return false;
210 case ':':
211 if(str[1] == '=' && isspace(str[2]))
212 return true;
213 case ',':
214 case '`':
215 case '(':
216 case '[':
217 case ')':
218 case ']':
219 case ';':
220 case '_':
221 return false;
222 case '+':
223 if(eq(str, "++%", 3)) {
224 --*tokens;
225 --str;
226 str[0] = '$';
227 str[3] = ':';
228 return true;
229 }
230 if(isdigit(str[1]) || str[1] == '.') {
231 ++*tokens;
232 return true;
233 }
234 if(isspace(str[1]))
235 break;
236 if(str[1] == '=' && isspace(str[2]))
237 break;
238 return false;
239 case '.':
240 if(isdigit(str[1]))
241 break;
242 return false;
243 case '-':
244 if(eq(str, "--%", 3)) {
245 --*tokens;
246 --str;
247 str[0] = '$';
248 str[3] = ':';
249 return true;
250 }
251 if(isalnum(str[1]) || str[1] == '.')
252 return true;
253 if(isspace(str[1]))
254 break;
255 if(str[1] == '=' && isspace(str[2]))
256 break;
257 return false;
258 case '\'':
259 case '\"':
260 case '{':
261 --str;
262 --*tokens;
263 str[0] = str[1];
264 str[1] = '&';
265 return true;
266 }
267
268 while(*str && (isalnum(*str) || *str == ':' || *str == '.'))
269 ++str;
270
271 // we flip xxx=... into =xxx objects...
272
273 if(*str == '=') {
274 --*tokens;
275 **tokens = '=';
276 *str = ' ';
277 }
278
279 return true;
280 }
281
282 Script::error::error(Script *img, unsigned line, const char *msg) :
283 OrderedObject(&img->errlist)
284 {
285 errmsg = img->dup(msg);
286 errline = line;
287 }
288
289 Script::Script() :
290 CountedObject(), memalloc()
291 {
292 errors = 0;
293 loop = 0;
294 lines = 0;
295 first = NULL;
296 global = NULL;
297 headers = NULL;
298 stack = NULL;
299 scripts = (LinkedObject **)alloc(sizeof(LinkedObject **) * Script::indexing);
300 memset(scripts, 0, sizeof(LinkedObject **) * Script::indexing);
301 }
302
303 Script::~Script()
304 {
305 if(stack)
306 delete[] stack;
307 shared = NULL;
308 }
309
310 void Script::errlog(unsigned line, const char *fmt, ...)
311 {
312 char text[65];
313 va_list args;
314
315 va_start(args, fmt);
316 vsnprintf(text, sizeof(text), fmt, args);
317
318 ++errors;
319 caddr_t mp = (caddr_t)alloc(sizeof(Script::error));
320 new(mp) Script::error(this, line, text);
321 va_end(args);
322 }
323
324 void Script::assign(Script::keyword_t *keyword)
325 {
326 while(keyword && keyword->name) {
327 keyword->next = keywords;
328 keywords = keyword;
329 ++keyword;
330 }
331 }
332
333 Script::keyword_t *Script::find(const char *cmd)
334 {
335 keyword_t *keyword = keywords;
336
337 while(keyword != NULL) {
338 if(eq(cmd, keyword->name))
339 break;
340 keyword = keyword->next;
341 }
342 return keyword;
343 }
344
345 void Script::init(void)
346 {
347 static keyword_t keywords[] = {
348 {"pause", (method_t)&methods::scrPause, (check_t)&checks::chkNop},
349 {"nop", (method_t)&methods::scrNop, (check_t)&checks::chkNop},
350 {"exit", (method_t)&methods::scrExit, (check_t)&checks::chkExit},
351 {"return", (method_t)&methods::scrReturn, (check_t)&checks::chkExit},
352 {"restart", (method_t)&methods::scrRestart, (check_t)&checks::chkExit},
353 {"goto", (method_t)&methods::scrGoto, (check_t)&checks::chkGoto},
354 {"gosub", (method_t)&methods::scrGosub, (check_t)&checks::chkGosub},
355 {"var", (method_t)&methods::scrVar, (check_t)&checks::chkVar},
356 {"const", (method_t)&methods::scrConst, (check_t)&checks::chkConst},
357 {"error", (method_t)&methods::scrError, (check_t)&checks::chkError},
358 {"clear", (method_t)&methods::scrClear, (check_t)&checks::chkClear},
359 {"push", (method_t)&methods::scrPush, (check_t)&checks::chkPush},
360 {"set", (method_t)&methods::scrSet, (check_t)&checks::chkSet},
361 {"add", (method_t)&methods::scrAdd, (check_t)&checks::chkSet},
362 {"pack", (method_t)&methods::scrPack, (check_t)&checks::chkPack},
363 {"expand", (method_t)&methods::scrExpand, (check_t)&checks::chkExpand},
364 {"do", (method_t)&methods::scrDo, (check_t)&checks::chkDo},
365 {"until", (method_t)&methods::scrUntil, (check_t)&checks::chkUntil},
366 {"break", (method_t)&methods::scrBreak, (check_t)&checks::chkBreak},
367 {"continue", (method_t)&methods::scrContinue, (check_t)&checks::chkContinue},
368 {"previous", (method_t)&methods::scrPrevious, (check_t)&checks::chkPrevious},
369 {"repeat", (method_t)&methods::scrRepeat, (check_t)&checks::chkPrevious},
370 {"index", (method_t)&methods::scrIndex, (check_t)&checks::chkIndex},
371 {"loop", (method_t)&methods::scrLoop, (check_t)&checks::chkLoop},
372 {"while", (method_t)&methods::scrWhile, (check_t)&checks::chkWhile},
373 {"for", (method_t)&methods::scrForeach, (check_t)&checks::chkForeach},
374 {"foreach", (method_t)&methods::scrForeach, (check_t)&checks::chkForeach},
375 {"case", (method_t)&methods::scrCase, (check_t)&checks::chkCase},
376 {"otherwise", (method_t)&methods::scrOtherwise, (check_t)&checks::chkOtherwise},
377 {"endcase", (method_t)&methods::scrEndcase, (check_t)&checks::chkEndcase},
378 {"if", (method_t)&methods::scrIf, (check_t)&checks::chkIf},
379 {"elif", (method_t)&methods::scrElif, (check_t)&checks::chkElif},
380 {"else", (method_t)&methods::scrElse, (check_t)&checks::chkElse},
381 {"endif", (method_t)&methods::scrEndif, (check_t)&checks::chkEndif},
382 {"expr", (method_t)&methods::scrExpr, (check_t)&checks::chkExpr},
383 {"strict", (method_t)NULL, (check_t)&checks::chkStrict},
384 {"apply", (method_t)NULL, (check_t)&checks::chkApply},
385 {"_ifthen", (method_t)&methods::scrWhen, (check_t)&checks::chkWhen},
386 {"_define", (method_t)&methods::scrDefine, (check_t)&checks::chkDefine},
387 {"_invoke", (method_t)&methods::scrInvoke, (check_t)&checks::chkInvoke},
388 {"_ref", (method_t)&methods::scrRef, (check_t)&checks::chkRef},
389 {NULL}
390 };
391
392 static bool initial = false;
393
394 if(!initial) {
395 initial = true;
396 assign(keywords);
397 }
398 }
399
400 Script::header *Script::find(Script *img, const char *id)
401 {
402 unsigned path = NamedObject::keyindex(id, Script::indexing);
403 linked_pointer<header> hp = img->scripts[path];
404
405 while(is(hp)) {
406 if(eq(hp->name, id))
407 break;
408
409 hp.next();
410 }
411 return *hp;
412 }
413
414 Script *Script::merge(const char *fn, Script *root)
415 {
416 Script *img = compile(fn, root);
417 if(!root || !img)
418 return img;
419
420 return merge(img, root);
421 }
422
423 Script *Script::merge(Script *img, Script *root)
424 {
425 assert(img != NULL && root != NULL);
426
427 for(unsigned index = 0; index < Script::indexing; ++index) {
428 header *prior = NULL;
429 linked_pointer<header> hp = img->scripts[index];
430 while(is(hp)) {
431 prior = *hp;
432 hp.next();
433 }
434 if(prior)
435 prior->link(static_cast<header *>(root->scripts[index]));
436 else
437 img->scripts[index] = root->scripts[index];
438 }
439
440 return img;
441 }
442
443 Script *Script::compile(const char *fn, Script *cfg)
444 {
445 return Script::append(NULL, fn, cfg);
446 }
447
448 Script *Script::append(Script *merge, const char *fn, Script *cfg)
449 {
450 // linked_pointer<script::strict> sp;
451
452 char **argv = new char *[256];
453 stringbuf<512> buffer;
454 Script *img;
455 charfile cf(fn, "r");
456 header *scr = NULL;
457 const char *name = "_init_";
458 Script::event *current;
459 Script::event *prior;
460 char *tokens;
461 const char *token = NULL;
462 char *arg;
463 unsigned lnum = 0;
464 line_t *line, *last;
465 bool section = false;
466 unsigned argc;
467 const char *err, *op;
468 char *assigned;
469 unsigned path;
470 bool indented, formed;
471 Script::header *invoke;
472 bool define = false;
473 bool when = false;
474 bool label = false;
475 unsigned pos;
476 const char *cp;
477 bool requires = false;
478 char *ep;
479 unsigned len;
480
481 if(!is(cf)) {
482 if(merge)
483 return merge;
484 return NULL;
485 }
486
487 if(merge)
488 img = merge;
489 else
490 img = new Script();
491
492 if(!img->stack)
493 img->stack = new line_t*[Script::stacking];
494
495 img->shared = cfg;
496 img->filename = strrchr(fn, '/');
497 if(!img->filename)
498 img->filename = strrchr(fn, '\\');
499 if(!img->filename)
500 img->filename = strrchr(fn, ':');
501 if(img->filename)
502 ++img->filename;
503 else
504 img->filename = fn;
505
506 initial:
507 current = NULL;
508 prior = NULL;
509 last = NULL;
510
511 scr = (header *)img->alloc(sizeof(header));
512 memset(scr, 0, sizeof(header));
513 scr->name = img->dup(name);
514 scr->events = NULL;
515 scr->first = NULL;
516 scr->resmask = 0;
517 scr->scoped = NULL;
518 keyword_t *keyword;
519 img->loop = 0;
520 char *temp;
521
522 // we drop additional initializers during append
523 if(!eq(name, "_init_") || !merge) {
524 path = NamedObject::keyindex(name, Script::indexing);
525 scr->enlist(&img->scripts[path]);
526 }
527
528 if(!img->first)
529 img->first = scr;
530
531 while(when || label || define || cf.readline(buffer)) {
532 if(define) {
533 indented = true;
534 goto parse;
535 }
536
537 if(when) {
538 indented = true;
539 when = false;
540 goto parse;
541 }
542
543 if(label) {
544 label = false;
545 indented = true;
546 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
547 if(!token)
548 continue;
549 goto parse;
550 }
551
552 img->lines = ++lnum;
553 img->thencheck = false;
554
555 indented = false;
556 tokens = buffer.c_mem();
557 if(isspace(*tokens)) {
558 while(isspace(*tokens))
559 ++tokens;
560 indented = true;
561 if(!preparse(&tokens)) {
562 img->errlog(lnum, "malformed line");
563 continue;
564 }
565 }
566
567 String::trim(buffer, " \t\r\n");
568
569 // if empty line or comment, continue...
570 if(isend(*buffer))
571 continue;
572
573 tokens = NULL;
574 token = String::token(buffer.c_mem(), &tokens, " \t", "{}\'\'\"\"");
575
576 if(eq(token, "endreq") || eq(token, "endrequires")) {
577 if(indented) {
578 img->errlog(lnum, "endreq cannot be indented");
579 continue;
580 }
581 requires = false;
582 continue;
583 }
584 else if(eq(token, "requires")) {
585 if(indented) {
586 img->errlog(lnum, "requires cannot be indented");
587 continue;
588 }
589 requires = true;
590 while(NULL != (token = String::token(NULL, &tokens, " \t", "{}\'\'\"\""))) {
591 bool rev = false;
592 if(*token == '!') {
593 rev = true;
594 ++token;
595 }
596 keyword = find(token);
597 invoke = find(img, token);
598 if(!invoke && is(img->shared))
599 invoke = find(*(img->shared), token);
600 if(!rev && (keyword || invoke)) {
601 requires = false;
602 break;
603 }
604 if(rev && !keyword && !invoke) {
605 requires = false;
606 break;
607 }
608 }
609 continue;
610 }
611
612 if(requires)
613 continue;
614
615 if(*token == '^' || *token == '-') {
616 current = (event *)img->alloc(sizeof(Script::event));
617 if(!prior)
618 prior = current;
619
620 last = NULL;
621 memset(current, 0, sizeof(event));
622 if(*token == '^')
623 current->enlist(&scr->events);
624 else
625 current->enlist(&scr->methods);
626 current->name = (char *)img->dup(++token);
627 current->first = NULL;
628 continue;
629 }
630
631 if(*token == '@') {
632 section = true;
633 name = token;
634 label = true;
635 goto closure;
636 }
637
638 if(eq(token, "template")) {
639 if(section) {
640 img->errlog(lnum, "templates must be before named sections");
641 continue;
642 }
643 if(indented) {
644 img->errlog(lnum, "templates cannot be indented");
645 continue;
646 }
647 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
648 if(!token)
649 img->errlog(lnum, "template must be named");
650 name = token;
651 goto closure;
652 }
653
654 if(eq(token, "define")) {
655 if(section) {
656 img->errlog(lnum, "defines must be before named sections");
657 continue;
658 }
659 if(indented) {
660 img->errlog(lnum, "define cannot be indented");
661 continue;
662 }
663 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
664 keyword = find(token);
665 if(keyword) {
666 img->errlog(lnum, "cannot redefine existing command");
667 continue;
668 }
669 define = true;
670 name = token;
671 goto closure;
672 }
673 if(!indented) {
674 img->errlog(lnum, "unindented statement");
675 continue;
676 }
677
678 parse:
679 assigned = NULL;
680 op = NULL;
681 if(*token == '%') {
682 assigned = img->dup(token);
683 op = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
684 if(op && (eq(op, ":=") || eq(op, "+=") || eq(op, "-=") || eq(op, "*=") || eq(op, "/=") || eq(op, "%=") || eq(op, "#=")))
685 token = "expr";
686 else if(op && (eq(op, "=") || eq(op, "==") || eq(op, "$="))) {
687 op = NULL;
688 token = "set";
689 }
690 else if(op && (eq(op, ".") || eq(op, ".="))) {
691 op = NULL;
692 token = "add";
693 }
694 else if(op && (eq(op, ",") || eq(op, ",="))) {
695 op = NULL;
696 token = "pack";
697 }
698 else {
699 img->errlog(lnum, "invalid assignment");
700 continue;
701 }
702 }
703 else if(*token == '$') {
704 assigned = img->dup(token);
705 token = "_ref";
706 }
707 invoke = NULL;
708 if(!iskeyword(token)) {
709 img->errlog(lnum, "invalid keyword");
710 continue;
711 }
712
713 if(define) {
714 define = false;
715 keyword = find("_define");
716 if(!keyword) {
717 img->errlog(lnum, "define unsupported");
718 continue;
719 }
720 }
721 else {
722 keyword = find(token);
723 if(!keyword && NULL != (invoke = find(img, token)))
724 keyword = find("_invoke");
725 if(!keyword && is(img->shared) && NULL != (invoke = find(*(img->shared), token)))
726 keyword = find("_invoke");
727
728 if(!keyword) {
729 img->errlog(lnum, "unknown keyword \"%s\"", token);
730 continue;
731 }
732 }
733
734 // protects in backparse
735 token = img->dup(token);
736
737 line = (line_t *)img->alloc(sizeof(line_t));
738 memset(line, 0, sizeof(line_t));
739 line->next = NULL;
740 line->lnum = lnum;
741 line->mask = 0;
742 if(invoke)
743 line->sub = invoke;
744 else
745 line->cmd = token;
746 line->loop = img->loop;
747 line->method = keyword->method;
748
749 formed = true;
750 argc = 0;
751 arg = NULL;
752
753 if(assigned)
754 argv[argc++] = assigned;
755
756 if(op)
757 argv[argc++] = img->dup(op);
758
759 while(argc < 249 && formed) {
760 if(!arg) {
761 formed = preparse(&tokens);
762 if(!formed)
763 break;
764 arg = (char *)String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
765 }
766 if(!arg)
767 break;
768
769 if(eq(token, "if")) {
770 if(eq(arg, "if")) {
771 formed = false;
772 break;
773 }
774 if(eq(arg, "then")) {
775 keyword = find("_ifthen");
776 line->method = keyword->method;
777 token = String::token(NULL, &tokens, " \t", "{}\'\'\"\"");
778 if(!token || !keyword || img->thencheck)
779 formed = false;
780 else
781 img->thencheck = when = true;
782 break;
783 }
784 }
785
786 ep = strchr(arg, '<');
787 if(*arg == '%' && ep) {
788 len = strlen(arg) + 10;
789 *(ep++) = 0;
790 temp = (char *)img->alloc(len);
791 String::set(temp, len, "$map/");
792 String::add(temp, len, ep);
793 ep = strchr(temp, '>');
794 if(ep)
795 *ep = ':';
796 String::add(temp, len, ++arg);
797 argv[argc++] = temp;
798 arg = NULL;
799 continue;
800 }
801
802 ep = strchr(arg, '[');
803 if(*arg == '%' && ep) {
804 len = strlen(arg) + 10;
805 *(ep++) = 0;
806 temp = (char *)img->alloc(len);
807 if(atoi(ep))
808 String::set(temp, len, "$offset/");
809 else
810 String::set(temp, len, "$find/");
811 String::add(temp, len, ep);
812 ep = strchr(temp, ']');
813 if(ep)
814 *ep = ':';
815 String::add(temp, len, ++arg);
816 argv[argc++] = temp;
817 arg = NULL;
818 continue;
819 }
820
821 ep = strchr(arg, '(');
822 if(*arg == '%' && ep) {
823 len = strlen(arg) + 10;
824 *(ep++) = 0;
825 temp = (char *)img->alloc(len);
826 String::set(temp, len, "$index/");
827 String::add(temp, len, ep);
828 ep = strchr(temp, ')');
829 if(ep)
830 *ep = ':';
831 String::add(temp, len, ++arg);
832 argv[argc++] = temp;
833 arg = NULL;
834 continue;
835 }
836
837 argv[argc++] = img->dup(arg);
838 arg = NULL;
839 }
840
841 if(!formed) {
842 img->errlog(lnum, "malformed statement or argument");
843 continue;
844 }
845
846 line->argc = argc;
847 argv[argc++] = NULL;
848 line->argv = (char **)img->alloc(sizeof(char *) * argc);
849 memcpy(line->argv, argv, sizeof(char *) * argc);
850
851 err = (*(keyword->check))(img, scr, line);
852 if(err) {
853 img->errlog(lnum, "%s", err);
854 continue;
855 }
856
857 pos = 0;
858 while(img->isStrict() && pos < line->argc) {
859 cp = line->argv[pos++];
860 if(!Script::strict::find(img, scr, cp))
861 img->errlog(lnum, "undefined symbol reference %s\n", cp);
862 }
863
864 // if compile-time only line, then no runtime method, so we skip...
865
866 if(line->method == (method_t)NULL)
867 continue;
868
869 // after error checking...we may add starting line...
870
871 if(last)
872 last->next = line;
873 else {
874 if(current) {
875 linked_pointer<event> ep = prior;
876 while(is(ep) && *ep != current) {
877 ep->first = line;
878 ep.next();
879 }
880 current->first = line;
881 }
882 else
883 scr->first = line;
884 }
885 last = line;
886 }
887
888 closure:
889 while(img->loop) {
890 line = img->stack[--img->loop];
891 img->errlog(line->lnum, "%s never completed loop", line->cmd);
892 }
893
894 keyword = find("_close");
895 if(keyword) {
896 err = (*(keyword->check))(img, scr, scr->first);
897 if(err)
898 img->errlog(lnum, "%s", err);
899 }
900
901 // sp = scr->scoped;
902 // while(is(sp)) {
903 // sp->put(stdout, scr->name);
904 // sp.next();
905 // }
906
907 if(!cf.eof())
908 goto initial;
909
910 delete[] argv;
911 delete[] img->stack;
912 img->stack = NULL;
913 // sp = img->global;
914 // while(is(sp)) {
915 // sp->put(stdout, "*");
916 // sp.next();
917 // }
918
919 return img;
920 }
921
922 bool Script::isEvent(header *scr, const char *id)
923 {
924 linked_pointer<event> ep = scr->events;
925
926 while(is(ep)) {
927 if(eq(ep->name, id))
928 return true;
929 ep.next();
930 }
931 return false;
932 }
933
934 bool Script::push(line_t *line)
935 {
936 if(loop < stacking) {
937 stack[loop++] = line;
938 return true;
939 }
940 return false;
941 }
942
943 Script::method_t Script::pull(void)
944 {
945 if(!loop)
946 return NULL;
947
948 return stack[--loop]->method;
949 }
950
951 Script::method_t Script::looping(void)
952 {
953 if(!loop)
954 return NULL;
955
956 return stack[loop - 1]->method;
957 }
958
959 void Script::strict::createVar(Script* image, Script::header *scr, const char *id)
960 {
961 assert(id && *id);
962 assert(scr != NULL);
963 assert(image != NULL);
964
965 linked_pointer<Script::strict> sp;
966 Script::strict *sym;
967
968 if(*id == '%' || *id == '=' || *id == '$')
969 ++id;
970
971 if(!image->global)
972 return;
973
974 if(*(scr->name) != '@') {
975 sp = scr->scoped;
976 while(is(sp)) {
977 if(ideq(sp->id, id))
978 return;
979 sp.next();
980 }
981 sym = (Script::strict *)image->zalloc(sizeof(Script::strict));
982 sym->enlist(&scr->scoped);
983 sym->id = id;
984 return;
985 }
986 sp = image->global;
987 while(is(sp)) {
988 if(ideq(sp->id, id))
989 return;
990 sp.next();
991 }
992 sym = (Script::strict *)image->zalloc(sizeof(Script::strict));
993 sym->enlist(&image->global);
994 sym->id = id;
995 }
996
997 void Script::strict::createSym(Script* image, Script::header *scr, const char *id)
998 {
999 assert(id && *id);
1000 assert(scr != NULL);
1001 assert(image != NULL);
1002
1003 linked_pointer<Script::strict> sp;
1004 Script::strict *sym;
1005
1006 if(*id == '%' || *id == '=' || *id == '$')
1007 ++id;
1008
1009 if(scr && !image->global)
1010 return;
1011
1012 if(*(scr->name) != '@') {
1013 sp = scr->scoped;
1014 while(is(sp)) {
1015 if(ideq(sp->id, id))
1016 return;
1017 sp.next();
1018 }
1019 }
1020 sp = image->global;
1021 while(is(sp)) {
1022 if(ideq(sp->id, id))
1023 return;
1024 sp.next();
1025 }
1026 sym = (Script::strict *)image->zalloc(sizeof(Script::strict));
1027 sym->enlist(&image->global);
1028 sym->id = id;
1029 }
1030
1031 void Script::strict::createAny(Script* image, Script::header *scr, const char *id)
1032 {
1033 assert(id && *id);
1034 assert(scr != NULL);
1035 assert(image != NULL);
1036
1037 linked_pointer<Script::strict> sp;
1038 Script::strict *sym;
1039
1040 if(*id == '%' || *id == '=' || *id == '$')
1041 ++id;
1042
1043 if(scr && !image->global)
1044 return;
1045
1046 if(*(scr->name) != '@') {
1047 sp = scr->scoped;
1048 while(is(sp)) {
1049 if(ideq(sp->id, id))
1050 return;
1051 sp.next();
1052 }
1053 }
1054 sp = image->global;
1055 while(is(sp)) {
1056 if(ideq(sp->id, id))
1057 return;
1058 sp.next();
1059 }
1060 sym = (Script::strict *)image->zalloc(sizeof(Script::strict));
1061 if(*(scr->name) != '@')
1062 sym->enlist(&scr->scoped);
1063 else
1064 sym->enlist(&image->global);
1065 sym->id = id;
1066 }
1067
1068 void Script::strict::createGlobal(Script *image, const char *id)
1069 {
1070 assert(id && *id);
1071 assert(image != NULL);
1072
1073 linked_pointer<Script::strict> sp;
1074 Script::strict *sym;
1075
1076 if(*id == '%' || *id == '=' || *id == '$')
1077 ++id;
1078
1079 sp = image->global;
1080 while(is(sp)) {
1081 if(ideq(sp->id, id))
1082 return;
1083 sp.next();
1084 }
1085 sym = (Script::strict *)image->zalloc(sizeof(Script::strict));
1086 sym->enlist(&image->global);
1087 sym->id = id;
1088 }
1089
1090 bool Script::strict::find(Script* image, Script::header *scr, const char *id)
1091 {
1092 assert(id && *id);
1093 assert(scr != NULL);
1094 assert(image != NULL);
1095
1096 char buf[64];
1097 const char *cp;
1098 char *ep;
1099 linked_pointer<Script::strict> sp;
1100
1101 if(!image->global)
1102 return true;
1103
1104 if(*id != '%' && *id != '$')
1105 return true;
1106
1107 if(eq(id, "$map/", 5)) {
1108 buf[0] = '%';
1109 String::set(buf + 1, sizeof(buf) - 1, id + 5);
1110 ep = strchr(buf, ':');
1111 if(ep)
1112 *ep = 0;
1113
1114 if(!find(image, scr, buf))
1115 return false;
1116 }
1117
1118 if(*id == '$') {
1119 cp = strchr(id, ':');
1120 if(cp)
1121 id = ++cp;
1122 else
1123 ++id;
1124 }
1125 else
1126 ++id;
1127
1128 if(*(scr->name) != '@') {
1129 sp = scr->scoped;
1130 while(is(sp)) {
1131 if(ideq(sp->id, id))
1132 return true;
1133 sp.next();
1134 }
1135 }
1136
1137 sp = image->global;
1138 while(is(sp)) {
1139 if(ideq(sp->id, id))
1140 return true;
1141 sp.next();
1142 }
1143 return false;
1144 }
1145
1146 void Script::strict::put(FILE *fp, const char *header)
1147 {
1148 assert(fp != NULL);
1149 assert(id != NULL);
1150
1151 const char *pid = id;
1152 if(header) {
1153 fputs(header, fp);
1154 fputc(':', fp);
1155 }
1156 while(*pid && *pid != ':')
1157 fputc(*(pid++), fp);
1158 fputc('\n', fp);
1159 }
1160
1161 unsigned Script::count(const char *data)
1162 {
1163 unsigned count = 0;
1164 char quote = 0;
1165 unsigned paren = 0;
1166
1167 if(!data || !*data)
1168 return 0;
1169
1170 ++count;
1171 while(*data) {
1172 if(*data == ',' && !quote && !paren)
1173 ++count;
1174 else if(*data == quote && !paren)
1175 quote = 0;
1176 else if(*data == '\"' && !paren)
1177 quote = *data;
1178 else if(*data == '(' && !quote)
1179 ++paren;
1180 else if(*data == ')' && !quote)
1181 --paren;
1182 ++data;
1183 }
1184 return count;
1185 }
1186
1187 void Script::copy(const char *list, char *item, unsigned size)
1188 {
1189 assert(size > 0);
1190 assert(item != NULL);
1191
1192 bool lead = false;
1193 char quote = 0;
1194 char prev = 0;
1195 unsigned paren = 0;
1196 bool leadparen = false;
1197
1198 if(!list || !*list || *list == ',') {
1199 *item = 0;
1200 return;
1201 }
1202
1203 while(isspace(*list))
1204 ++list;
1205
1206 if(*list == '(') {
1207 leadparen = true;
1208 ++paren;
1209 ++list;
1210 }
1211 else {
1212 if(*list == '\"' || *list == '\'') {
1213 quote = *(list++);
1214 lead = true;
1215 }
1216 }
1217
1218 while(--size && *list) {
1219 if(paren == 1 && leadparen && *list == ')' && !quote)
1220 break;
1221 if(*list == '(')
1222 ++paren;
1223 else if(*list == ')')
1224 --paren;
1225 if(*list == ',' && !quote && !paren)
1226 break;
1227 if(*list == quote && lead)
1228 break;
1229 if(*list == quote) {
1230 *(item++) = quote;
1231 break;
1232 }
1233 if(!quote && *list == '\"' && prev == '=' && !paren)
1234 quote = *list;
1235 prev = *item;
1236 *(item++) = *(list++);
1237 }
1238 *item = 0;
1239 }
1240
1241 unsigned Script::offset(const char *list, unsigned index)
1242 {
1243 const char *cp = get(list, index);
1244 return cp - list;
1245 }
1246
1247 const char *Script::get(const char *list, unsigned index)
1248 {
1249 char quote = 0;
1250 unsigned paren = 0;
1251
1252 if(!list || !*list)
1253 return NULL;
1254
1255 if(!index)
1256 return list;
1257
1258 while(*list && index) {
1259 if(*list == ',' && !quote && !paren) {
1260 --index;
1261 if(!index)
1262 return ++list;
1263 } else if(*list == quote)
1264 quote = 0;
1265 else if(*list == '(' && !quote)
1266 ++paren;
1267 else if(*list == ')' && !quote)
1268 --paren;
1269 else if(*list == '\"' && !paren)
1270 quote = *list;
1271 ++list;
1272 }
1273 return NULL;
1274 }
1275
1276 char *Script::get(char *list, unsigned index)
1277 {
1278 char quote = 0;
1279 unsigned paren = 0;
1280
1281 if(!list || !*list)
1282 return NULL;
1283
1284 if(!index)
1285 return list;
1286
1287 while(*list && index) {
1288 if(*list == ',' && !quote && !paren) {
1289 --index;
1290 if(!index)
1291 return ++list;
1292 } else if(*list == quote)
1293 quote = 0;
1294 else if(*list == '(' && !quote)
1295 ++paren;
1296 else if(*list == ')' && !quote)
1297 --paren;
1298 else if(*list == '\"' && !paren)
1299 quote = *list;
1300 ++list;
1301 }
1302 return NULL;
1303 }
1304
#define BAYONNE_NAMESPACE
GNU Bayonne library namespace.