1 // command.cpp: implements the parsing and execution of a tiny script language which
2 // is largely backwards compatible with the quake console language.
3
4 #define CPP_ENGINE_COMMAND 1
5 #include "engine.h"
6
7 hashnameset<ident> idents; // contains ALL vars/commands/aliases
8 vector<ident *> identmap;
9 ident *dummyident = NULL;
10
11 int identflags = 0;
12
13 enum
14 {
15 MAXARGS = 25,
16 MAXRESULTS = 7,
17 MAXCOMARGS = 16
18 };
19
20 VARN(0, numargs, _numargs, MAXARGS, 0, 0);
21
freearg(tagval & v)22 static inline void freearg(tagval &v)
23 {
24 switch(v.type)
25 {
26 case VAL_STR: delete[] v.s; break;
27 case VAL_CODE: if(v.code[-1] == CODE_START) delete[] (uchar *)&v.code[-1]; break;
28 }
29 }
30
forcenull(tagval & v)31 static inline void forcenull(tagval &v)
32 {
33 switch(v.type)
34 {
35 case VAL_NULL: return;
36 }
37 freearg(v);
38 v.setnull();
39 }
40
forcefloat(tagval & v)41 static inline float forcefloat(tagval &v)
42 {
43 float f = 0.0f;
44 switch(v.type)
45 {
46 case VAL_INT: f = v.i; break;
47 case VAL_STR: case VAL_MACRO: case VAL_CSTR: f = parsefloat(v.s); break;
48 case VAL_FLOAT: return v.f;
49 }
50 freearg(v);
51 v.setfloat(f);
52 return f;
53 }
54
forceint(tagval & v)55 static inline int forceint(tagval &v)
56 {
57 int i = 0;
58 switch(v.type)
59 {
60 case VAL_FLOAT: i = v.f; break;
61 case VAL_STR: case VAL_MACRO: case VAL_CSTR: i = parseint(v.s); break;
62 case VAL_INT: return v.i;
63 }
64 freearg(v);
65 v.setint(i);
66 return i;
67 }
68
forcestr(tagval & v)69 static inline const char *forcestr(tagval &v)
70 {
71 const char *s = "";
72 switch(v.type)
73 {
74 case VAL_FLOAT: s = floatstr(v.f); break;
75 case VAL_INT: s = intstr(v.i); break;
76 case VAL_MACRO: case VAL_CSTR: s = v.s; break;
77 case VAL_STR: return v.s;
78 }
79 freearg(v);
80 v.setstr(newstring(s));
81 return s;
82 }
83
forcearg(tagval & v,int type)84 static inline void forcearg(tagval &v, int type)
85 {
86 switch(type)
87 {
88 case RET_STR: if(v.type != VAL_STR) forcestr(v); break;
89 case RET_INT: if(v.type != VAL_INT) forceint(v); break;
90 case RET_FLOAT: if(v.type != VAL_FLOAT) forcefloat(v); break;
91 }
92 }
93
cleanup()94 void tagval::cleanup()
95 {
96 freearg(*this);
97 }
98
freeargs(tagval * args,int & oldnum,int newnum)99 static inline void freeargs(tagval *args, int &oldnum, int newnum)
100 {
101 for(int i = newnum; i < oldnum; i++) freearg(args[i]);
102 oldnum = newnum;
103 }
104
cleancode(ident & id)105 static inline void cleancode(ident &id)
106 {
107 if(id.code)
108 {
109 id.code[0] -= 0x100;
110 if(int(id.code[0]) < 0x100) delete[] id.code;
111 id.code = NULL;
112 }
113 }
114
115 struct nullval : tagval
116 {
nullvalnullval117 nullval() { setnull(); }
118 } nullval;
119 tagval noret = nullval, *commandret = &noret;
120
clear_command()121 void clear_command()
122 {
123 enumerate(idents, ident, i,
124 {
125 if(i.type==ID_ALIAS)
126 {
127 DELETEA(i.name);
128 i.forcenull();
129 DELETEA(i.code);
130 }
131 });
132 }
133
clearoverride(ident & i)134 void clearoverride(ident &i)
135 {
136 switch(i.type)
137 {
138 case ID_ALIAS:
139 if(i.valtype==VAL_STR)
140 {
141 if(!i.val.s[0]) break;
142 delete[] i.val.s;
143 }
144 cleancode(i);
145 i.valtype = VAL_STR;
146 i.val.s = newstring(i.def.s);
147 break;
148 case ID_VAR:
149 *i.storage.i = i.def.i;
150 i.changed();
151 break;
152 case ID_FVAR:
153 *i.storage.f = i.def.f;
154 i.changed();
155 break;
156 case ID_SVAR:
157 delete[] *i.storage.s;
158 *i.storage.s = i.def.s;
159 i.changed();
160 break;
161 }
162 }
163
clearoverrides()164 void clearoverrides()
165 {
166 enumerate(idents, ident, i, clearoverride(i));
167 }
168
169 static bool initedidents = false;
170 static vector<ident> *identinits = NULL;
171
addident(const ident & id)172 static inline ident *addident(const ident &id)
173 {
174 if(!initedidents)
175 {
176 if(!identinits) identinits = new vector<ident>;
177 identinits->add(id);
178 return NULL;
179 }
180 ident &def = idents.access(id.name, id);
181 def.index = identmap.length();
182 return identmap.add(&def);
183 }
184
initidents()185 static bool initidents()
186 {
187 initedidents = true;
188 for(int i = 0; i < MAXARGS; i++)
189 {
190 defformatstring(argname, "arg%d", i+1);
191 newident(argname, IDF_ARG);
192 }
193 dummyident = newident("//dummy", IDF_UNKNOWN);
194 if(identinits)
195 {
196 loopv(*identinits) addident((*identinits)[i]);
197 DELETEP(identinits);
198 }
199 return true;
200 }
201 UNUSED static bool forceinitidents = initidents();
202
203 static const char *sourcefile = NULL, *sourcestr = NULL;
204
debugline(const char * p,const char * fmt)205 static const char *debugline(const char *p, const char *fmt)
206 {
207 if(!sourcestr) return fmt;
208 int num = 1;
209 const char *line = sourcestr;
210 for(;;)
211 {
212 const char *end = strchr(line, '\n');
213 if(!end) end = line + strlen(line);
214 if(p >= line && p <= end)
215 {
216 static bigstring buf;
217 char color[] = { '\0', '\0', '\0' };
218 if(fmt[0] == '\f') { memcpy(color, fmt, 2); fmt += strlen(color); }
219 if(sourcefile) formatstring(buf, "%s%s:%d: %s", color, sourcefile, num, fmt);
220 else formatstring(buf, "%s%d: %s", color, num, fmt);
221 return buf;
222 }
223 if(!*end) break;
224 line = end + 1;
225 num++;
226 }
227 return fmt;
228 }
229
230 static struct identlink
231 {
232 ident *id;
233 identlink *next;
234 int usedargs;
235 identstack *argstack;
236 } noalias = { NULL, NULL, (1<<MAXARGS)-1, NULL }, *aliasstack = &noalias;
237
238 VAR(0, dbgalias, 0, 20, 1000);
239
debugalias()240 static void debugalias()
241 {
242 if(!dbgalias) return;
243 int total = 0, depth = 0;
244 for(identlink *l = aliasstack; l != &noalias; l = l->next) total++;
245 for(identlink *l = aliasstack; l != &noalias; l = l->next)
246 {
247 ident *id = l->id;
248 ++depth;
249 if(depth < dbgalias) conoutft(CON_DEBUG, " %d) %s", total-depth+1, id->name);
250 else if(l->next == &noalias) conoutft(CON_DEBUG, depth == dbgalias ? " %d) %s" : " ..%d) %s", total-depth+1, id->name);
251 }
252 }
253
254 static int nodebug = 0;
255
256 static void debugcode(const char *fmt, ...) PRINTFARGS(1, 2);
257
debugcode(const char * fmt,...)258 static void debugcode(const char *fmt, ...)
259 {
260 if(nodebug) return;
261
262 defvformatbigstring(msg, fmt, fmt);
263 conoutft(CON_DEBUG, "%s", msg);
264
265 debugalias();
266 }
267
268 static void debugcodeline(const char *p, const char *fmt, ...) PRINTFARGS(2, 3);
269
debugcodeline(const char * p,const char * fmt,...)270 static void debugcodeline(const char *p, const char *fmt, ...)
271 {
272 if(nodebug) return;
273
274 defvformatbigstring(msg, fmt, debugline(p, fmt));
275 conoutft(CON_DEBUG, "%s", msg);
276
277 debugalias();
278 }
279
280 ICOMMAND(0, nodebug, "e", (uint *body), { nodebug++; executeret(body, *commandret); nodebug--; });
281
addident(ident * id)282 void addident(ident *id)
283 {
284 addident(*id);
285 }
286
pusharg(ident & id,const tagval & v,identstack & stack)287 static inline void pusharg(ident &id, const tagval &v, identstack &stack)
288 {
289 stack.val = id.val;
290 stack.valtype = id.valtype;
291 stack.next = id.stack;
292 id.stack = &stack;
293 id.setval(v);
294 cleancode(id);
295 }
296
poparg(ident & id)297 static inline void poparg(ident &id)
298 {
299 if(!id.stack) return;
300 identstack *stack = id.stack;
301 if(id.valtype == VAL_STR) delete[] id.val.s;
302 id.setval(*stack);
303 cleancode(id);
304 id.stack = stack->next;
305 }
306
undoarg(ident & id,identstack & stack)307 static inline void undoarg(ident &id, identstack &stack)
308 {
309 identstack *prev = id.stack;
310 stack.val = id.val;
311 stack.valtype = id.valtype;
312 stack.next = prev;
313 id.stack = prev->next;
314 id.setval(*prev);
315 cleancode(id);
316 }
317
318 #define UNDOFLAG (1<<MAXARGS)
319 #define UNDOARGS \
320 identstack argstack[MAXARGS]; \
321 identlink *prevstack = aliasstack; \
322 identlink aliaslink; \
323 for(int undos = 0; prevstack != &noalias; prevstack = prevstack->next) \
324 { \
325 if(prevstack->usedargs & UNDOFLAG) ++undos; \
326 else if(undos > 0) --undos; \
327 else \
328 { \
329 prevstack = prevstack->next; \
330 for(int argmask = aliasstack->usedargs & ~UNDOFLAG, i = 0; argmask; argmask >>= 1, i++) if(argmask&1) \
331 undoarg(*identmap[i], argstack[i]); \
332 aliaslink.id = aliasstack->id; \
333 aliaslink.next = aliasstack; \
334 aliaslink.usedargs = UNDOFLAG | prevstack->usedargs; \
335 aliaslink.argstack = prevstack->argstack; \
336 aliasstack = &aliaslink; \
337 break; \
338 } \
339 } \
340
redoarg(ident & id,const identstack & stack)341 static inline void redoarg(ident &id, const identstack &stack)
342 {
343 identstack *prev = stack.next;
344 prev->val = id.val;
345 prev->valtype = id.valtype;
346 id.stack = prev;
347 id.setval(stack);
348 cleancode(id);
349 }
350
351 #define REDOARGS \
352 if(aliasstack == &aliaslink) \
353 { \
354 prevstack->usedargs |= aliaslink.usedargs & ~UNDOFLAG; \
355 aliasstack = aliaslink.next; \
356 for(int argmask = aliasstack->usedargs & ~UNDOFLAG, i = 0; argmask; argmask >>= 1, i++) if(argmask&1) \
357 redoarg(*identmap[i], argstack[i]); \
358 }
359
360 ICOMMAND(0, push, "rTe", (ident *id, tagval *v, uint *code),
361 {
362 if(id->type != ID_ALIAS || id->index < MAXARGS) return;
363 identstack stack;
364 pusharg(*id, *v, stack);
365 v->type = VAL_NULL;
366 id->flags &= ~IDF_UNKNOWN;
367 executeret(code, *commandret);
368 poparg(*id);
369 });
370
pushalias(ident & id,identstack & stack)371 static inline void pushalias(ident &id, identstack &stack)
372 {
373 if(id.type == ID_ALIAS && id.index >= MAXARGS)
374 {
375 pusharg(id, nullval, stack);
376 id.flags &= ~IDF_UNKNOWN;
377 }
378 }
379
popalias(ident & id)380 static inline void popalias(ident &id)
381 {
382 if(id.type == ID_ALIAS && id.index >= MAXARGS) poparg(id);
383 }
384
385 KEYWORD(0, local, ID_LOCAL);
386
checknumber(const char * s)387 static inline bool checknumber(const char *s)
388 {
389 if(isdigit(s[0])) return true;
390 else switch(s[0])
391 {
392 case '+': case '-': return isdigit(s[1]) || (s[1] == '.' && isdigit(s[2]));
393 case '.': return isdigit(s[1]) != 0;
394 default: return false;
395 }
396 }
checknumber(const stringslice & s)397 static inline bool checknumber(const stringslice &s) { return checknumber(s.str); }
398
newident(const T & name,int flags,int level=0)399 template<class T> static inline ident *newident(const T &name, int flags, int level = 0)
400 {
401 ident *id = idents.access(name);
402 if(!id)
403 {
404 if(checknumber(name))
405 {
406 debugcode("\frNumber %.*s is not a valid identifier name", stringlen(name), stringptr(name));
407 return dummyident;
408 }
409 id = addident(ident(ID_ALIAS, newstring(name), flags, level));
410 }
411 return id;
412 }
413
forceident(tagval & v)414 static inline ident *forceident(tagval &v)
415 {
416 switch(v.type)
417 {
418 case VAL_IDENT: return v.id;
419 case VAL_MACRO: case VAL_CSTR:
420 {
421 ident *id = newident(v.s, IDF_UNKNOWN);
422 v.setident(id);
423 return id;
424 }
425 case VAL_STR:
426 {
427 ident *id = newident(v.s, IDF_UNKNOWN);
428 delete[] v.s;
429 v.setident(id);
430 return id;
431 }
432 }
433 freearg(v);
434 v.setident(dummyident);
435 return dummyident;
436 }
437
newident(const char * name,int flags,int level)438 ident *newident(const char *name, int flags, int level)
439 {
440 return newident<const char *>(name, flags, level);
441 }
442
writeident(const char * name,int flags,int level)443 ident *writeident(const char *name, int flags, int level)
444 {
445 ident *id = newident(name, flags, level);
446 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
447 {
448 pusharg(*id, nullval, aliasstack->argstack[id->index]);
449 aliasstack->usedargs |= 1<<id->index;
450 }
451 return id;
452 }
453
readident(const char * name)454 ident *readident(const char *name)
455 {
456 ident *id = idents.access(name);
457 if(id && id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
458 return NULL;
459 return id;
460 }
461
printreadonly(ident * id)462 void printreadonly(ident *id)
463 {
464 debugcode("\frVariable %s is read-only", id->name);
465 }
466
printeditonly(ident * id)467 void printeditonly(ident *id)
468 {
469 debugcode("\frVariable %s is only directly modifiable in editmode", id->name);
470 }
471
resetvar(char * name)472 void resetvar(char *name)
473 {
474 ident *id = idents.access(name);
475 if(!id) return;
476 if(id->flags&IDF_READONLY) printreadonly(id);
477 else if(id->flags&IDF_WORLD || id->flags&IDF_CLIENT || id->flags&IDF_SERVER) debugcode("\frVariable %s cannot be reset", id->name);
478 else clearoverride(*id);
479 }
480
481 COMMAND(0, resetvar, "s");
482
setarg(ident & id,tagval & v)483 static inline void setarg(ident &id, tagval &v)
484 {
485 if(aliasstack->usedargs&(1<<id.index))
486 {
487 if(id.valtype == VAL_STR) delete[] id.val.s;
488 id.setval(v);
489 cleancode(id);
490 }
491 else
492 {
493 pusharg(id, v, aliasstack->argstack[id.index]);
494 aliasstack->usedargs |= 1<<id.index;
495 }
496 }
497
setalias(ident & id,tagval & v,bool world)498 static inline void setalias(ident &id, tagval &v, bool world)
499 {
500 if(world && !(id.flags&IDF_WORLD) && !(id.flags&IDF_UNKNOWN))
501 {
502 debugcode("\frCannot redefine %s as a world alias", id.name);
503 return;
504 }
505 #ifndef STANDALONE
506 if(!(identflags&IDF_WORLD) && !editmode && (world || (id.flags&IDF_WORLD && !(id.flags&IDF_REWRITE))))
507 {
508 printeditonly(&id);
509 return;
510 }
511 #endif
512 if(id.valtype == VAL_STR) delete[] id.val.s;
513 id.setval(v);
514 cleancode(id);
515 id.flags = (world || id.flags&IDF_WORLD ? IDF_WORLD : 0)|(id.flags&IDF_PERSIST ? IDF_PERSIST : 0);
516 #ifndef STANDALONE
517 client::editvar(&id, !(identflags&IDF_WORLD));
518 #endif
519 }
520
setalias(const char * name,tagval & v,bool world)521 static void setalias(const char *name, tagval &v, bool world)
522 {
523 ident *id = idents.access(name);
524 if(id)
525 {
526 switch(id->type)
527 {
528 case ID_ALIAS:
529 if(id->index < MAXARGS) setarg(*id, v);
530 else setalias(*id, v, world);
531 return;
532 case ID_VAR:
533 setvarchecked(id, v.getint());
534 break;
535 case ID_FVAR:
536 setfvarchecked(id, v.getfloat());
537 break;
538 case ID_SVAR:
539 setsvarchecked(id, v.getstr());
540 break;
541 default:
542 debugcode("\frCannot redefine builtin %s with an alias", id->name);
543 break;
544 }
545 freearg(v);
546 }
547 else if(checknumber(name))
548 {
549 debugcode("\frCannot alias number %s", name);
550 freearg(v);
551 }
552 else
553 {
554 #ifndef STANDALONE
555 if(!(identflags&IDF_WORLD) && !editmode && world)
556 {
557 debugcode("\frCannot create %s as a world alias outside editmode", name);
558 return;
559 }
560 #endif
561 id = addident(ident(ID_ALIAS, newstring(name), v, identflags|(world ? IDF_WORLD : 0), 0));
562 #ifndef STANDALONE
563 client::editvar(id, !(identflags&IDF_WORLD));
564 #endif
565 }
566 }
567
alias(const char * name,const char * action,bool world)568 void alias(const char *name, const char *action, bool world)
569 {
570 tagval v;
571 v.setstr(newstring(action));
572 setalias(name, v, world);
573 }
574
alias(const char * name,tagval & v,bool world)575 void alias(const char *name, tagval &v, bool world)
576 {
577 setalias(name, v, world);
578 }
579
580 ICOMMAND(0, alias, "sT", (const char *name, tagval *v),
581 {
582 setalias(name, *v, (identflags&IDF_WORLD)!=0);
583 v->type = VAL_NULL;
584 });
585
worldalias(const char * name,const char * action)586 void worldalias(const char *name, const char *action)
587 {
588 alias(name, action, true);
589 }
590 COMMAND(0, worldalias, "ss");
591
loadalias(const char * name,const char * fname,int * world)592 void loadalias(const char *name, const char *fname, int *world)
593 {
594 string s;
595 copystring(s, fname);
596 char *buf = loadfile(s, NULL);
597 if(!buf)
598 {
599 conoutf("\frCould not read %s", fname);
600 return;
601 }
602 tagval v;
603 v.setstr(buf);
604 setalias(name, v, *world!=0);
605 }
606 COMMAND(0, loadalias, "ssi");
607
608 // variables and commands are registered through globals, see cube.h
609
variable(const char * name,int min,int cur,int max,int * storage,identfun fun,int flags,int level)610 int variable(const char *name, int min, int cur, int max, int *storage, identfun fun, int flags, int level)
611 {
612 addident(ident(ID_VAR, name, min, cur, max, storage, (void *)fun, flags, level));
613 return cur;
614 }
615
fvariable(const char * name,float min,float cur,float max,float * storage,identfun fun,int flags,int level)616 float fvariable(const char *name, float min, float cur, float max, float *storage, identfun fun, int flags, int level)
617 {
618 addident(ident(ID_FVAR, name, min, cur, max, storage, (void *)fun, flags, level));
619 return cur;
620 }
621
svariable(const char * name,const char * cur,char ** storage,identfun fun,int flags,int level)622 char *svariable(const char *name, const char *cur, char **storage, identfun fun, int flags, int level)
623 {
624 addident(ident(ID_SVAR, name, newstring(cur), storage, (void *)fun, flags, level));
625 return newstring(cur);
626 }
627
628 struct defvar : identval
629 {
630 char *name;
631 uint *onchange;
632
defvardefvar633 defvar() : name(NULL), onchange(NULL) {}
634
~defvardefvar635 ~defvar()
636 {
637 DELETEA(name);
638 if(onchange) freecode(onchange);
639 }
640
changeddefvar641 static void changed(ident *id)
642 {
643 defvar *v = (defvar *)id->storage.p;
644 if(v->onchange) execute(v->onchange);
645 }
646 };
647
648 hashnameset<defvar> defvars;
649
650 #define DEFVAR(cmdname, fmt, args, typebody, body) \
651 ICOMMAND(0, cmdname, fmt, args, \
652 { \
653 if(idents.access(name)) \
654 { \
655 if(!defvars.access(name)) debugcode("Cannot redefine %s as a variable", name); \
656 else { typebody; } \
657 return; \
658 } \
659 name = newstring(name); \
660 defvar &def = defvars[name]; \
661 def.name = name; \
662 def.onchange = onchange[0] ? compilecode(onchange) : NULL; \
663 body; \
664 });
665
666 #define DEFIVAR(cmdname, flags) \
667 DEFVAR(cmdname, "siiis", (char *name, int *min, int *cur, int *max, char *onchange), \
668 setvar(name, *cur, true, true), \
669 def.i = variable(name, *min, *cur, *max, &def.i, def.onchange ? defvar::changed : NULL, flags))
670 #define DEFFVAR(cmdname, flags) \
671 DEFVAR(cmdname, "sfffs", (char *name, float *min, float *cur, float *max, char *onchange), \
672 setfvar(name, *cur, true, true), \
673 def.f = fvariable(name, *min, *cur, *max, &def.f, def.onchange ? defvar::changed : NULL, flags))
674 #define DEFSVAR(cmdname, flags) \
675 DEFVAR(cmdname, "sss", (char *name, char *cur, char *onchange), \
676 setsvar(name, cur, true, true), \
677 def.s = svariable(name, cur, &def.s, def.onchange ? defvar::changed : NULL, flags))
678
679 DEFIVAR(defvar, IDF_COMPLETE);
680 DEFIVAR(defvarp, IDF_COMPLETE|IDF_PERSIST);
681 DEFIVAR(defhvar, IDF_COMPLETE|IDF_HEX);
682 DEFIVAR(defhvarp, IDF_COMPLETE|IDF_PERSIST|IDF_HEX);
683 DEFFVAR(deffvar, IDF_COMPLETE);
684 DEFFVAR(deffvarp, IDF_COMPLETE|IDF_PERSIST);
685 DEFSVAR(defsvar, IDF_COMPLETE);
686 DEFSVAR(defsvarp, IDF_COMPLETE|IDF_PERSIST);
687
688 #define GETVAR(id, vartype, name, retval) \
689 ident *id = idents.access(name); \
690 if(!id || id->type!=vartype) return retval;
691
clampvar(ident * id,int val,int minval,int maxval,bool msg)692 int clampvar(ident *id, int val, int minval, int maxval, bool msg)
693 {
694 int oldval = val;
695 if(id->flags&IDF_HEX && uint(maxval) == 0xFFFFFFFFU)
696 {
697 if(uint(val) < uint(minval)) val = uint(minval);
698 else if(uint(val) > uint(maxval)) val = uint(maxval);
699 if(msg && val != oldval) debugcode("\frValid range for %s is 0x%X..0x%X", id->name, uint(minval), uint(maxval));
700 return val;
701 }
702 else if(val < minval) val = minval;
703 else if(val > maxval) val = maxval;
704 if(msg && val != oldval) debugcode(id->flags&IDF_HEX ?
705 (minval <= 255 ? "\frValid range for %s is %d..0x%X" : "\frValid range for %s is 0x%X..0x%X") : "\frValid range for %s is %d..%d",
706 id->name, minval, maxval);
707 return val;
708 }
709
clampfvar(ident * id,float val,float minval,float maxval,bool msg)710 float clampfvar(ident *id, float val, float minval, float maxval, bool msg)
711 {
712 float oldval = val;
713 if(val < minval) val = minval;
714 else if(val > maxval) val = maxval;
715 if(msg && val != oldval) debugcode("\frValid range for %s is %s..%s", id->name, floatstr(minval), floatstr(maxval));
716 return val;
717 }
718
setvar(const char * name,int i,bool dofunc,bool def,bool force)719 void setvar(const char *name, int i, bool dofunc, bool def, bool force)
720 {
721 GETVAR(id, ID_VAR, name, );
722 if(force) *id->storage.i = i;
723 else *id->storage.i = clampvar(id, i, id->minval, id->maxval, false);
724 if(def || versioning)
725 {
726 id->def.i = *id->storage.i;
727 if(versioning == 2) id->bin.i = *id->storage.i;
728 }
729 if(dofunc) id->changed();
730 #ifndef STANDALONE
731 if(versioning && id->flags&IDF_SERVER) setvar(&id->name[3], i, dofunc, def);
732 #endif
733 }
734
setfvar(const char * name,float f,bool dofunc,bool def,bool force)735 void setfvar(const char *name, float f, bool dofunc, bool def, bool force)
736 {
737 GETVAR(id, ID_FVAR, name, );
738 if(force) *id->storage.f = f;
739 else *id->storage.f = clampfvar(id, f, id->minvalf, id->maxvalf, false);
740 if(def || versioning)
741 {
742 id->def.f = *id->storage.f;
743 if(versioning == 2) id->bin.f = *id->storage.f;
744 }
745 if(dofunc) id->changed();
746 #ifndef STANDALONE
747 if(versioning && id->flags&IDF_SERVER) setfvar(&id->name[3], f, dofunc, def);
748 #endif
749 }
750
setsvar(const char * name,const char * str,bool dofunc,bool def)751 void setsvar(const char *name, const char *str, bool dofunc, bool def)
752 {
753 GETVAR(id, ID_SVAR, name, );
754 delete[] *id->storage.s;
755 *id->storage.s = newstring(str);
756 if(def || versioning)
757 {
758 delete[] id->def.s;
759 id->def.s = newstring(*id->storage.s);
760 if(versioning == 2)
761 {
762 delete[] id->bin.s;
763 id->bin.s = newstring(*id->storage.s);
764 }
765 }
766 if(dofunc) id->changed();
767 #ifndef STANDALONE
768 if(versioning && id->flags&IDF_SERVER) setsvar(&id->name[3], str, dofunc, def);
769 #endif
770 }
771
setvarflag(const char * s,const char * v,int flag,const char * msg,bool alias)772 void setvarflag(const char *s, const char *v, int flag, const char *msg, bool alias)
773 {
774 ident *id = idents.access(s);
775 if(!id || (alias && id->type != ID_ALIAS))
776 {
777 if(verbose) conoutf("\frAdding %s of %s failed as it is not available", msg, s);
778 return;
779 }
780 bool on = false;
781 if(isnumeric(*v)) on = atoi(v) != 0;
782 else if(!strcasecmp("false", v)) on = false;
783 else if(!strcasecmp("true", v)) on = true;
784 else if(!strcasecmp("on", v)) on = true;
785 else if(!strcasecmp("off", v)) on = false;
786 if(on && !(id->flags&flag)) id->flags |= flag;
787 else if(!on && id->flags&flag) id->flags &= ~flag;
788 }
789
790 ICOMMAND(0, setcomplete, "ss", (char *s, char *t), setvarflag(s, t, IDF_COMPLETE, "complete", false));
791 ICOMMAND(0, setpersist, "ss", (char *s, char *t), setvarflag(s, t, IDF_PERSIST, "persist", true));
792
setvardesc(const char * s,const char * v,const char * f)793 void setvardesc(const char *s, const char *v, const char *f)
794 {
795 ident *id = idents.access(s);
796 if(!id)
797 {
798 if(verbose) conoutf("\frAdding description of %s failed as it is not available", s);
799 return;
800 }
801 DELETEA(id->desc);
802 if(v && *v) id->desc = newstring(v);
803 loopvrev(id->fields)
804 {
805 DELETEA(id->fields[i]);
806 id->fields.remove(i);
807 }
808 if(f && *f) explodelist(f, id->fields);
809 }
810 ICOMMAND(0, setdesc, "sss", (char *s, char *t, char *f), setvardesc(s, t, f));
811
setvarlevel(const char * s,int level)812 void setvarlevel(const char *s, int level)
813 {
814 static string varname;
815 if(strlen(s) < 3 || strncmp(s, "sv_", 3)) formatstring(varname, "sv_%s", s);
816 else copystring(varname, s);
817 ident *id = idents.access(varname);
818 if(!id)
819 {
820 if(verbose) conoutf("\frSetting level of %s failed as it is not available", s);
821 return;
822 }
823 if(!(id->flags&IDF_SERVER))
824 {
825 if(verbose) conoutf("\frSetting level of %s failed as it is not a server side variable", s);
826 return;
827 }
828 id->level = level;
829 }
830 ICOMMAND(0, setvarlevel, "si", (char *s, int *level), setvarlevel(s, *level));
831
getvar(const char * name)832 int getvar(const char *name)
833 {
834 GETVAR(id, ID_VAR, name, 0);
835 switch(id->type)
836 {
837 case ID_VAR: return *id->storage.i;
838 case ID_FVAR: return int(*id->storage.f);
839 case ID_SVAR: return atoi(*id->storage.s);
840 case ID_ALIAS: return id->getint();
841 default: break;
842 }
843 return 0;
844 }
845
getvartype(const char * name)846 int getvartype(const char *name)
847 {
848 ident *id = idents.access(name);
849 if(!id) return -1;
850 return id->type;
851 }
852
getvarflags(const char * name)853 int getvarflags(const char *name)
854 {
855 ident *id = idents.access(name);
856 if(!id) return -1;
857 return id->flags;
858 }
859
getvarlevel(const char * name)860 int getvarlevel(const char *name)
861 {
862 ident *id = idents.access(name);
863 if(!id) return -1;
864 return id->level;
865 }
866
getvarmin(const char * name)867 int getvarmin(const char *name)
868 {
869 ident *id = idents.access(name);
870 if(!id) return 0;
871 if(id) switch(id->type)
872 {
873 case ID_VAR: return id->minval;
874 case ID_FVAR: return int(id->minvalf);
875 }
876 return 0;
877 }
878
getvarmax(const char * name)879 int getvarmax(const char *name)
880 {
881 ident *id = idents.access(name);
882 if(!id) return 0;
883 if(id) switch(id->type)
884 {
885 case ID_VAR: return id->maxval;
886 case ID_FVAR: return int(id->maxvalf);
887 }
888 return 0;
889 }
890
getfvarmin(const char * name)891 float getfvarmin(const char *name)
892 {
893 ident *id = idents.access(name);
894 if(id) switch(id->type)
895 {
896 case ID_VAR: return id->minval;
897 case ID_FVAR: return id->minvalf;
898 }
899 return 0;
900 }
901
getfvarmax(const char * name)902 float getfvarmax(const char *name)
903 {
904 ident *id = idents.access(name);
905 if(id) switch(id->type)
906 {
907 case ID_VAR: return id->maxval;
908 case ID_FVAR: return id->maxvalf;
909 }
910 return 0;
911 }
912
getvardef(const char * name,bool rb)913 int getvardef(const char *name, bool rb)
914 {
915 ident *id = getident(name);
916 if(!id) return 0;
917 switch(id->type)
918 {
919 case ID_VAR: return (rb ? id->bin : id->def).i;
920 case ID_FVAR: return int((rb ? id->bin : id->def).f);
921 case ID_SVAR: return atoi((rb ? id->bin : id->def).s);
922 case ID_ALIAS: return id->getint();
923 default: break;
924 }
925 return 0;
926 }
927
getfvardef(const char * name,bool rb)928 float getfvardef(const char *name, bool rb)
929 {
930 ident *id = getident(name);
931 if(!id) return 0.f;
932 switch(id->type)
933 {
934 case ID_VAR: return float((rb ? id->bin : id->def).i);
935 case ID_FVAR: return (rb ? id->bin : id->def).f;
936 case ID_SVAR: return atof((rb ? id->bin : id->def).s);
937 case ID_ALIAS: return id->getfloat();
938 default: break;
939 }
940 return 0.f;
941 }
942
getsvardef(const char * name,bool rb)943 const char *getsvardef(const char *name, bool rb)
944 {
945 ident *id = getident(name);
946 if(!id) return "";
947 switch(id->type)
948 {
949 case ID_VAR: return intstr((rb ? id->bin : id->def).i);
950 case ID_FVAR: return floatstr((rb ? id->bin : id->def).f);
951 case ID_SVAR: return (rb ? id->bin : id->def).s;
952 case ID_ALIAS: return id->getstr();
953 default: break;
954 }
955 return "";
956 }
957
getvardesc(const char * name)958 const char *getvardesc(const char *name)
959 {
960 ident *id = getident(name);
961 if(!id || !id->desc) return "";
962 return id->desc;
963 }
964
getvarfields(const char * name,int prop)965 void getvarfields(const char *name, int prop)
966 {
967 ident *id = getident(name);
968 if(!id)
969 {
970 result("");
971 return;
972 }
973 if(prop < 0) intret(id->fields.length());
974 else if(id->fields.inrange(prop)) result(id->fields[prop]);
975 else result("");
976 }
977
getvarargs(const char * name)978 const char *getvarargs(const char *name)
979 {
980 ident *id = getident(name);
981 if(!id || !id->args) return "";
982 return id->args;
983 }
984
985 ICOMMAND(0, getvar, "s", (char *n), intret(getvar(n)));
986 ICOMMAND(0, getvartype, "s", (char *n), intret(getvartype(n)));
987 ICOMMAND(0, getvarflags, "s", (char *n), intret(getvarflags(n)));
988 ICOMMAND(0, getvarlevel, "s", (char *n), intret(getvarlevel(n)));
989 ICOMMAND(0, getvarmin, "s", (char *n), intret(getvarmin(n)));
990 ICOMMAND(0, getvarmax, "s", (char *n), intret(getvarmax(n)));
991 ICOMMAND(0, getfvarmin, "s", (char *s), floatret(getfvarmin(s)));
992 ICOMMAND(0, getfvarmax, "s", (char *s), floatret(getfvarmax(s)));
993 ICOMMAND(0, getvardef, "si", (char *n, int *b), intret(getvardef(n, *b!=0)));
994 ICOMMAND(0, getfvardef, "si", (char *n, int *b), floatret(getfvardef(n, *b!=0)));
995 ICOMMAND(0, getsvardef, "si", (char *n, int *b), result(getsvardef(n, *b!=0)));
996 ICOMMAND(0, getvardesc, "s", (char *n), result(getvardesc(n)));
997 ICOMMAND(0, getvarfields, "sb", (char *n, int *p), getvarfields(n, *p));
998 ICOMMAND(0, getvarargs, "s", (char *n), result(getvarargs(n)));
999
identexists(const char * name)1000 bool identexists(const char *name) { return idents.access(name)!=NULL; }
1001 ICOMMAND(0, identexists, "s", (char *s), intret(identexists(s) ? 1 : 0));
1002
getident(const char * name)1003 ident *getident(const char *name) { return idents.access(name); }
1004
touchvar(const char * name)1005 void touchvar(const char *name)
1006 {
1007 ident *id = idents.access(name);
1008 if(id) switch(id->type)
1009 {
1010 case ID_VAR:
1011 case ID_FVAR:
1012 case ID_SVAR:
1013 id->changed();
1014 break;
1015 }
1016 }
1017
getalias(const char * name)1018 const char *getalias(const char *name)
1019 {
1020 ident *i = idents.access(name);
1021 return i && i->type==ID_ALIAS && (i->index >= MAXARGS || aliasstack->usedargs&(1<<i->index)) ? i->getstr() : "";
1022 }
1023
1024 ICOMMAND(0, getalias, "s", (char *s), result(getalias(s)));
1025
1026 #ifndef STANDALONE
1027 #define CHECKVAR(argstr) \
1028 if(!versioning) \
1029 { \
1030 if(!(identflags&IDF_WORLD) && !editmode && id->flags&IDF_WORLD && !(id->flags&IDF_REWRITE)) \
1031 { \
1032 printeditonly(id); \
1033 return; \
1034 } \
1035 if(id->flags&IDF_CLIENT) \
1036 { \
1037 if((identflags&IDF_WORLD) && !(id->flags&IDF_WORLD)) \
1038 { \
1039 debugcode("\frCannot set variable %s from map config", id->name); \
1040 return; \
1041 } \
1042 if(client::sendcmd(2, id->name, argstr)) return; \
1043 } \
1044 }
1045 #endif
1046
setvarchecked(ident * id,int val)1047 void setvarchecked(ident *id, int val)
1048 {
1049 if(id->flags&IDF_READONLY) printreadonly(id);
1050 else
1051 {
1052 #ifndef STANDALONE
1053 CHECKVAR(intstr(val))
1054 #endif
1055 val = clampvar(id, val, id->minval, id->maxval);
1056 *id->storage.i = val;
1057 if(versioning)
1058 {
1059 id->def.i = val;
1060 if(versioning == 2) id->bin.i = val;
1061 }
1062 id->changed(); // call trigger function if available
1063 #ifndef STANDALONE
1064 client::editvar(id, !(identflags&IDF_WORLD));
1065 if(versioning && id->flags&IDF_SERVER) setvar(&id->name[3], val);
1066 #endif
1067 }
1068 }
1069
setvarchecked(ident * id,tagval * args,int numargs)1070 static inline void setvarchecked(ident *id, tagval *args, int numargs)
1071 {
1072 int val = forceint(args[0]);
1073 if(id->flags&IDF_HEX && numargs > 1)
1074 {
1075 val = (val << 16) | (forceint(args[1])<<8);
1076 if(numargs > 2) val |= forceint(args[2]);
1077 }
1078 setvarchecked(id, val);
1079 }
1080
setfvarchecked(ident * id,float val)1081 void setfvarchecked(ident *id, float val)
1082 {
1083 if(id->flags&IDF_READONLY) printreadonly(id);
1084 else
1085 {
1086 #ifndef STANDALONE
1087 CHECKVAR(floatstr(val))
1088 #endif
1089 val = clampfvar(id, val, id->minvalf, id->maxvalf);
1090 *id->storage.f = val;
1091 if(versioning)
1092 {
1093 id->def.f = val;
1094 if(versioning == 2) id->bin.f = val;
1095 }
1096 id->changed();
1097 #ifndef STANDALONE
1098 client::editvar(id, !(identflags&IDF_WORLD));
1099 if(versioning && id->flags&IDF_SERVER) setfvar(&id->name[3], val, true);
1100 #endif
1101 }
1102 }
1103
setsvarchecked(ident * id,const char * val)1104 void setsvarchecked(ident *id, const char *val)
1105 {
1106 if(id->flags&IDF_READONLY) printreadonly(id);
1107 else
1108 {
1109 #ifndef STANDALONE
1110 CHECKVAR(val)
1111 #endif
1112 delete[] *id->storage.s;
1113 *id->storage.s = newstring(val);
1114 if(versioning)
1115 {
1116 delete[] id->def.s;
1117 id->def.s = newstring(val);
1118 if(versioning == 2)
1119 {
1120 delete[] id->bin.s;
1121 id->bin.s = newstring(val);
1122 }
1123 }
1124 id->changed();
1125 #ifndef STANDALONE
1126 client::editvar(id, !(identflags&IDF_WORLD));
1127 if(versioning && id->flags&IDF_SERVER) setsvar(&id->name[3], val, true);
1128 #endif
1129 }
1130 }
1131
addcommand(const char * name,identfun fun,const char * args,int type,int flags,int level)1132 bool addcommand(const char *name, identfun fun, const char *args, int type, int flags, int level)
1133 {
1134 uint argmask = 0;
1135 int numargs = 0;
1136 bool limit = true;
1137 if(args) for(const char *fmt = args; *fmt; fmt++) switch(*fmt)
1138 {
1139 case 'i': case 'b': case 'f': case 'g': case 'F': case 't': case 'T': case 'E': case 'N': case 'D': if(numargs < MAXARGS) numargs++; break;
1140 case 'S': case 's': case 'e': case 'r': case '$': if(numargs < MAXARGS) { argmask |= 1<<numargs; numargs++; } break;
1141 case '1': case '2': case '3': case '4': if(numargs < MAXARGS) fmt -= *fmt-'0'+1; break;
1142 case 'C': case 'V': limit = false; break;
1143 default: fatal("Builtin %s declared with illegal type: %s", name, args); break;
1144 }
1145 if(limit && numargs > MAXCOMARGS) fatal("Builtin %s declared with too many args: %d", name, numargs);
1146 addident(ident(type, name, args, argmask, numargs, (void *)fun, flags, level));
1147 return false;
1148 }
1149
parsestring(const char * p)1150 const char *parsestring(const char *p)
1151 {
1152 for(; *p; p++) switch(*p)
1153 {
1154 case '\r':
1155 case '\n':
1156 case '\"':
1157 return p;
1158 case '^':
1159 if(*++p) break;
1160 return p;
1161 }
1162 return p;
1163 }
1164
unescapestring(char * dst,const char * src,const char * end)1165 int unescapestring(char *dst, const char *src, const char *end)
1166 {
1167 char *start = dst;
1168 while(src < end)
1169 {
1170 int c = *src++;
1171 if(c == '^')
1172 {
1173 if(src >= end) break;
1174 int e = *src++;
1175 switch(e)
1176 {
1177 case 'n': *dst++ = '\n'; break;
1178 case 't': *dst++ = '\t'; break;
1179 case 'f': *dst++ = '\f'; break;
1180 default: *dst++ = e; break;
1181 }
1182 }
1183 else *dst++ = c;
1184 }
1185 *dst = '\0';
1186 return dst - start;
1187 }
1188
conc(vector<char> & buf,tagval * v,int n,bool space,const char * prefix=NULL,int prefixlen=0)1189 static char *conc(vector<char> &buf, tagval *v, int n, bool space, const char *prefix = NULL, int prefixlen = 0)
1190 {
1191 if(prefix)
1192 {
1193 buf.put(prefix, prefixlen);
1194 if(space && n) buf.add(' ');
1195 }
1196 loopi(n)
1197 {
1198 const char *s = "";
1199 int len = 0;
1200 switch(v[i].type)
1201 {
1202 case VAL_INT: s = intstr(v[i].i); break;
1203 case VAL_FLOAT: s = floatstr(v[i].f); break;
1204 case VAL_STR: case VAL_CSTR: s = v[i].s; break;
1205 case VAL_MACRO: s = v[i].s; len = v[i].code[-1]>>8; goto haslen;
1206 }
1207 len = int(strlen(s));
1208 haslen:
1209 buf.put(s, len);
1210 if(i == n-1) break;
1211 if(space) buf.add(' ');
1212 }
1213 buf.add('\0');
1214 return buf.getbuf();
1215 }
1216
conc(tagval * v,int n,bool space,const char * prefix,int prefixlen)1217 char *conc(tagval *v, int n, bool space, const char *prefix, int prefixlen)
1218 {
1219 static int vlen[MAXARGS];
1220 static char numbuf[3*MAXSTRLEN];
1221 int len = prefixlen, numlen = 0, i = 0;
1222 for(; i < n; i++) switch(v[i].type)
1223 {
1224 case VAL_MACRO: len += (vlen[i] = v[i].code[-1]>>8); break;
1225 case VAL_STR: case VAL_CSTR: len += (vlen[i] = int(strlen(v[i].s))); break;
1226 case VAL_INT:
1227 if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
1228 intformat(&numbuf[numlen], v[i].i);
1229 numlen += (vlen[i] = strlen(&numbuf[numlen]));
1230 break;
1231 case VAL_FLOAT:
1232 if(numlen + MAXSTRLEN > int(sizeof(numbuf))) goto overflow;
1233 floatformat(&numbuf[numlen], v[i].f);
1234 numlen += (vlen[i] = strlen(&numbuf[numlen]));
1235 break;
1236 default: vlen[i] = 0; break;
1237 }
1238 overflow:
1239 if(space) len += max(prefix ? i : i-1, 0);
1240 char *buf = newstring(len + numlen);
1241 int offset = 0, numoffset = 0;
1242 if(prefix)
1243 {
1244 memcpy(buf, prefix, prefixlen);
1245 offset += prefixlen;
1246 if(space && i) buf[offset++] = ' ';
1247 }
1248 loopj(i)
1249 {
1250 if(v[j].type == VAL_INT || v[j].type == VAL_FLOAT)
1251 {
1252 memcpy(&buf[offset], &numbuf[numoffset], vlen[j]);
1253 numoffset += vlen[j];
1254 }
1255 else if(vlen[j]) memcpy(&buf[offset], v[j].s, vlen[j]);
1256 offset += vlen[j];
1257 if(j==i-1) break;
1258 if(space) buf[offset++] = ' ';
1259 }
1260 buf[offset] = '\0';
1261 if(i < n)
1262 {
1263 char *morebuf = conc(&v[i], n-i, space, buf, offset);
1264 delete[] buf;
1265 return morebuf;
1266 }
1267 return buf;
1268 }
1269
skipcomments(const char * & p)1270 static inline void skipcomments(const char *&p)
1271 {
1272 for(;;)
1273 {
1274 p += strspn(p, " \t\r");
1275 if(p[0]!='/' || p[1]!='/') break;
1276 p += strcspn(p, "\n\0");
1277 }
1278 }
1279
1280 static vector<char> strbuf[4];
1281 static int stridx = 0;
1282
cutstring(const char * & p,stringslice & s)1283 static inline void cutstring(const char *&p, stringslice &s)
1284 {
1285 p++;
1286 const char *end = parsestring(p);
1287 int maxlen = int(end-p) + 1;
1288
1289 stridx = (stridx + 1)%4;
1290 vector<char> &buf = strbuf[stridx];
1291 if(buf.alen < maxlen) buf.growbuf(maxlen);
1292
1293 s.str = buf.buf;
1294 s.len = unescapestring(buf.buf, p, end);
1295 p = end;
1296 if(*p=='\"') p++;
1297 }
1298
cutstring(const char * & p)1299 static inline char *cutstring(const char *&p)
1300 {
1301 p++;
1302 const char *end = parsestring(p);
1303 char *buf = newstring(end-p);
1304 unescapestring(buf, p, end);
1305 p = end;
1306 if(*p=='\"') p++;
1307 return buf;
1308 }
1309
parsetext(const char * & p)1310 char *parsetext(const char *&p)
1311 {
1312 p += strspn(p, " \t\r");
1313 if(*p == '"') return cutstring(p);
1314 else
1315 {
1316 const char *start = p;
1317 p += strcspn(p, " ;\t\r\n\0");
1318 return newstring(start, p-start);
1319 }
1320 }
1321
parseword(const char * p)1322 static inline const char *parseword(const char *p)
1323 {
1324 const int maxbrak = 100;
1325 static char brakstack[maxbrak];
1326 int brakdepth = 0;
1327 for(;; p++)
1328 {
1329 p += strcspn(p, "\"/;()[] \t\r\n\0");
1330 switch(p[0])
1331 {
1332 case '"': case ';': case ' ': case '\t': case '\r': case '\n': case '\0': return p;
1333 case '/': if(p[1] == '/') return p; break;
1334 case '[': case '(': if(brakdepth >= maxbrak) return p; brakstack[brakdepth++] = p[0]; break;
1335 case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return p; break;
1336 case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return p; break;
1337 }
1338 }
1339 return p;
1340 }
1341
cutword(const char * & p,stringslice & s)1342 static inline void cutword(const char *&p, stringslice &s)
1343 {
1344 s.str = p;
1345 p = parseword(p);
1346 s.len = int(p-s.str);
1347 }
1348
cutword(const char * & p)1349 static inline char *cutword(const char *&p)
1350 {
1351 const char *word = p;
1352 p = parseword(p);
1353 return p!=word ? newstring(word, p-word) : NULL;
1354 }
1355
1356 #define retcode(type, defaultret) ((type) >= VAL_ANY ? ((type) == VAL_CSTR ? RET_STR : (defaultret)) : (type) << CODE_RET)
1357 #define retcodeint(type) retcode(type, RET_INT)
1358 #define retcodefloat(type) retcode(type, RET_FLOAT)
1359 #define retcodeany(type) retcode(type, 0)
1360 #define retcodestr(type) ((type) >= VAL_ANY ? RET_STR : (type) << CODE_RET)
1361
compilestr(vector<uint> & code,const char * word,int len,bool macro=false)1362 static inline void compilestr(vector<uint> &code, const char *word, int len, bool macro = false)
1363 {
1364 if(len <= 3 && !macro)
1365 {
1366 uint op = CODE_VALI|RET_STR;
1367 loopi(len) op |= uint(uchar(word[i]))<<((i+1)*8);
1368 code.add(op);
1369 return;
1370 }
1371 code.add((macro ? CODE_MACRO : CODE_VAL|RET_STR)|(len<<8));
1372 code.put((const uint *)word, len/sizeof(uint));
1373 size_t endlen = len%sizeof(uint);
1374 union
1375 {
1376 char c[sizeof(uint)];
1377 uint u;
1378 } end;
1379 end.u = 0;
1380 memcpy(end.c, word + len - endlen, endlen);
1381 code.add(end.u);
1382 }
1383
compilestr(vector<uint> & code)1384 static inline void compilestr(vector<uint> &code) { code.add(CODE_VALI|RET_STR); }
compilestr(vector<uint> & code,const stringslice & word,bool macro=false)1385 static inline void compilestr(vector<uint> &code, const stringslice &word, bool macro = false) { compilestr(code, word.str, word.len, macro); }
compilestr(vector<uint> & code,const char * word,bool macro=false)1386 static inline void compilestr(vector<uint> &code, const char *word, bool macro = false) { compilestr(code, word, int(strlen(word)), macro); }
1387
compileunescapestring(vector<uint> & code,const char * & p,bool macro=false)1388 static inline void compileunescapestring(vector<uint> &code, const char *&p, bool macro = false)
1389 {
1390 p++;
1391 const char *end = parsestring(p);
1392 code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
1393 char *buf = (char *)code.reserve(int(end-p)/sizeof(uint) + 1).buf;
1394 int len = unescapestring(buf, p, end);
1395 memset(&buf[len], 0, sizeof(uint) - len%sizeof(uint));
1396 code.last() |= len<<8;
1397 code.advance(len/sizeof(uint) + 1);
1398 p = end;
1399 if(*p == '\"') p++;
1400 }
1401
compileint(vector<uint> & code,int i=0)1402 static inline void compileint(vector<uint> &code, int i = 0)
1403 {
1404 if(i >= -0x800000 && i <= 0x7FFFFF)
1405 code.add(CODE_VALI|RET_INT|(i<<8));
1406 else
1407 {
1408 code.add(CODE_VAL|RET_INT);
1409 code.add(i);
1410 }
1411 }
1412
compilenull(vector<uint> & code)1413 static inline void compilenull(vector<uint> &code)
1414 {
1415 code.add(CODE_VALI|RET_NULL);
1416 }
1417
1418 static uint emptyblock[VAL_ANY][2] =
1419 {
1420 { CODE_START + 0x100, CODE_EXIT|RET_NULL },
1421 { CODE_START + 0x100, CODE_EXIT|RET_INT },
1422 { CODE_START + 0x100, CODE_EXIT|RET_FLOAT },
1423 { CODE_START + 0x100, CODE_EXIT|RET_STR }
1424 };
1425
compileblock(vector<uint> & code)1426 static inline void compileblock(vector<uint> &code)
1427 {
1428 code.add(CODE_EMPTY);
1429 }
1430
1431 static void compilestatements(vector<uint> &code, const char *&p, int rettype, int brak = '\0', int prevargs = 0);
1432
compileblock(vector<uint> & code,const char * p,int rettype=RET_NULL,int brak='\\0')1433 static inline const char *compileblock(vector<uint> &code, const char *p, int rettype = RET_NULL, int brak = '\0')
1434 {
1435 int start = code.length();
1436 code.add(CODE_BLOCK);
1437 code.add(CODE_OFFSET|((start+2)<<8));
1438 if(p) compilestatements(code, p, VAL_ANY, brak);
1439 if(code.length() > start + 2)
1440 {
1441 code.add(CODE_EXIT|rettype);
1442 code[start] |= uint(code.length() - (start + 1))<<8;
1443 }
1444 else
1445 {
1446 code.setsize(start);
1447 code.add(CODE_EMPTY|rettype);
1448 }
1449 return p;
1450 }
1451
compileident(vector<uint> & code,ident * id=dummyident)1452 static inline void compileident(vector<uint> &code, ident *id = dummyident)
1453 {
1454 code.add((id->index < MAXARGS ? CODE_IDENTARG : CODE_IDENT)|(id->index<<8));
1455 }
1456
compileident(vector<uint> & code,const stringslice & word)1457 static inline void compileident(vector<uint> &code, const stringslice &word)
1458 {
1459 compileident(code, newident(word, IDF_UNKNOWN));
1460 }
1461
compileint(vector<uint> & code,const stringslice & word)1462 static inline void compileint(vector<uint> &code, const stringslice &word)
1463 {
1464 compileint(code, word.len ? parseint(word.str) : 0);
1465 }
1466
compilefloat(vector<uint> & code,float f=0.0f)1467 static inline void compilefloat(vector<uint> &code, float f = 0.0f)
1468 {
1469 if(int(f) == f && f >= -0x800000 && f <= 0x7FFFFF)
1470 code.add(CODE_VALI|RET_FLOAT|(int(f)<<8));
1471 else
1472 {
1473 union { float f; uint u; } conv;
1474 conv.f = f;
1475 code.add(CODE_VAL|RET_FLOAT);
1476 code.add(conv.u);
1477 }
1478 }
1479
compilefloat(vector<uint> & code,const stringslice & word)1480 static inline void compilefloat(vector<uint> &code, const stringslice &word)
1481 {
1482 compilefloat(code, word.len ? parsefloat(word.str) : 0.0f);
1483 }
1484
getbool(const char * s)1485 static inline bool getbool(const char *s)
1486 {
1487 switch(s[0])
1488 {
1489 case '+': case '-':
1490 switch(s[1])
1491 {
1492 case '0': break;
1493 case '.': return !isdigit(s[2]) || parsefloat(s) != 0;
1494 default: return true;
1495 }
1496 // fall-through
1497 case '0':
1498 {
1499 char *end;
1500 int val = int(strtoul((char *)s, &end, 0));
1501 if(val) return true;
1502 switch(*end)
1503 {
1504 case 'e': case '.': return parsefloat(s) != 0;
1505 default: return false;
1506 }
1507 }
1508 case '.': return !isdigit(s[1]) || parsefloat(s) != 0;
1509 case '\0': return false;
1510 default: return true;
1511 }
1512 }
1513
getbool(const tagval & v)1514 static inline bool getbool(const tagval &v)
1515 {
1516 switch(v.type)
1517 {
1518 case VAL_FLOAT: return v.f!=0;
1519 case VAL_INT: return v.i!=0;
1520 case VAL_STR: case VAL_MACRO: case VAL_CSTR: return getbool(v.s);
1521 default: return false;
1522 }
1523 }
1524
compileval(vector<uint> & code,int wordtype,const stringslice & word=stringslice (NULL,0))1525 static inline void compileval(vector<uint> &code, int wordtype, const stringslice &word = stringslice(NULL, 0))
1526 {
1527 switch(wordtype)
1528 {
1529 case VAL_CANY: if(word.len) compilestr(code, word, true); else compilenull(code); break;
1530 case VAL_CSTR: compilestr(code, word, true); break;
1531 case VAL_ANY: if(word.len) compilestr(code, word); else compilenull(code); break;
1532 case VAL_STR: compilestr(code, word); break;
1533 case VAL_FLOAT: compilefloat(code, word); break;
1534 case VAL_INT: compileint(code, word); break;
1535 case VAL_COND: if(word.len) compileblock(code, word.str); else compilenull(code); break;
1536 case VAL_CODE: compileblock(code, word.str); break;
1537 case VAL_IDENT: compileident(code, word); break;
1538 default: break;
1539 }
1540 }
1541
1542 static stringslice unusedword(NULL, 0);
1543 static bool compilearg(vector<uint> &code, const char *&p, int wordtype, int prevargs = MAXRESULTS, stringslice &word = unusedword);
1544
compilelookup(vector<uint> & code,const char * & p,int ltype,int prevargs=MAXRESULTS)1545 static void compilelookup(vector<uint> &code, const char *&p, int ltype, int prevargs = MAXRESULTS)
1546 {
1547 stringslice lookup;
1548 switch(*++p)
1549 {
1550 case '(':
1551 case '[':
1552 if(!compilearg(code, p, VAL_CSTR, prevargs)) goto invalid;
1553 break;
1554 case '$':
1555 compilelookup(code, p, VAL_CSTR, prevargs);
1556 break;
1557 case '\"':
1558 cutstring(p, lookup);
1559 goto lookupid;
1560 default:
1561 {
1562 cutword(p, lookup);
1563 if(!lookup.len) goto invalid;
1564 lookupid:
1565 ident *id = newident(lookup, IDF_UNKNOWN);
1566 if(id) switch(id->type)
1567 {
1568 case ID_VAR:
1569 code.add(CODE_IVAR|retcodeint(ltype)|(id->index<<8));
1570 switch(ltype)
1571 {
1572 case VAL_POP: code.pop(); break;
1573 case VAL_CODE: code.add(CODE_COMPILE); break;
1574 case VAL_IDENT: code.add(CODE_IDENTU); break;
1575 }
1576 return;
1577 case ID_FVAR:
1578 code.add(CODE_FVAR|retcodefloat(ltype)|(id->index<<8));
1579 switch(ltype)
1580 {
1581 case VAL_POP: code.pop(); break;
1582 case VAL_CODE: code.add(CODE_COMPILE); break;
1583 case VAL_IDENT: code.add(CODE_IDENTU); break;
1584 }
1585 return;
1586 case ID_SVAR:
1587 switch(ltype)
1588 {
1589 case VAL_POP: return;
1590 case VAL_CANY: case VAL_CSTR: case VAL_CODE: case VAL_IDENT: case VAL_COND:
1591 code.add(CODE_SVARM|(id->index<<8));
1592 break;
1593 default:
1594 code.add(CODE_SVAR|retcodestr(ltype)|(id->index<<8));
1595 break;
1596 }
1597 goto done;
1598 case ID_ALIAS:
1599 switch(ltype)
1600 {
1601 case VAL_POP: return;
1602 case VAL_CANY: case VAL_COND:
1603 code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|(id->index<<8));
1604 break;
1605 case VAL_CSTR: case VAL_CODE: case VAL_IDENT:
1606 code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|RET_STR|(id->index<<8));
1607 break;
1608 default:
1609 code.add((id->index < MAXARGS ? CODE_LOOKUPARG : CODE_LOOKUP)|retcodestr(ltype)|(id->index<<8));
1610 break;
1611 }
1612 goto done;
1613 case ID_COMMAND:
1614 {
1615 int comtype = CODE_COM, numargs = 0;
1616 if(prevargs >= MAXRESULTS) code.add(CODE_ENTER);
1617 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
1618 {
1619 case 'S': compilestr(code); numargs++; break;
1620 case 's': compilestr(code, NULL, 0, true); numargs++; break;
1621 case 'i': compileint(code); numargs++; break;
1622 case 'b': compileint(code, INT_MIN); numargs++; break;
1623 case 'f': compilefloat(code); numargs++; break;
1624 case 'g': compilefloat(code, -FLT_MAX); numargs++; break;
1625 case 'F': code.add(CODE_DUP|RET_FLOAT); numargs++; break;
1626 case 'E':
1627 case 'T':
1628 case 't': compilenull(code); numargs++; break;
1629 case 'e': compileblock(code); numargs++; break;
1630 case 'r': compileident(code); numargs++; break;
1631 case '$': compileident(code, id); numargs++; break;
1632 case 'N': compileint(code, -1); numargs++; break;
1633 #ifndef STANDALONE
1634 case 'D': comtype = CODE_COMD; numargs++; break;
1635 #endif
1636 case 'C': comtype = CODE_COMC; goto compilecomv;
1637 case 'V': comtype = CODE_COMV; goto compilecomv;
1638 case '1': case '2': case '3': case '4': break;
1639 }
1640 code.add(comtype|retcodeany(ltype)|(id->index<<8));
1641 code.add((prevargs >= MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG) | retcodeany(ltype));
1642 goto done;
1643 compilecomv:
1644 code.add(comtype|retcodeany(ltype)|(numargs<<8)|(id->index<<13));
1645 code.add((prevargs >= MAXRESULTS ? CODE_EXIT : CODE_RESULT_ARG) | retcodeany(ltype));
1646 goto done;
1647 }
1648 default: goto invalid;
1649 }
1650 compilestr(code, lookup, true);
1651 break;
1652 }
1653 }
1654 switch(ltype)
1655 {
1656 case VAL_CANY: case VAL_COND:
1657 code.add(CODE_LOOKUPMU);
1658 break;
1659 case VAL_CSTR: case VAL_CODE: case VAL_IDENT:
1660 code.add(CODE_LOOKUPMU|RET_STR);
1661 break;
1662 default:
1663 code.add(CODE_LOOKUPU|retcodeany(ltype));
1664 break;
1665 }
1666 done:
1667 switch(ltype)
1668 {
1669 case VAL_POP: code.add(CODE_POP); break;
1670 case VAL_CODE: code.add(CODE_COMPILE); break;
1671 case VAL_COND: code.add(CODE_COND); break;
1672 case VAL_IDENT: code.add(CODE_IDENTU); break;
1673 }
1674 return;
1675 invalid:
1676 switch(ltype)
1677 {
1678 case VAL_POP: break;
1679 case VAL_NULL: case VAL_ANY: case VAL_CANY: case VAL_WORD: case VAL_COND: compilenull(code); break;
1680 default: compileval(code, ltype); break;
1681 }
1682 }
1683
compileblockstr(vector<uint> & code,const char * str,const char * end,bool macro)1684 static bool compileblockstr(vector<uint> &code, const char *str, const char *end, bool macro)
1685 {
1686 int start = code.length();
1687 code.add(macro ? CODE_MACRO : CODE_VAL|RET_STR);
1688 char *buf = (char *)code.reserve((end-str)/sizeof(uint)+1).buf;
1689 int len = 0;
1690 while(str < end)
1691 {
1692 int n = strcspn(str, "\r/\"@]\0");
1693 memcpy(&buf[len], str, n);
1694 len += n;
1695 str += n;
1696 switch(*str)
1697 {
1698 case '\r': str++; break;
1699 case '\"':
1700 {
1701 const char *start = str;
1702 str = parsestring(str+1);
1703 if(*str=='\"') str++;
1704 memcpy(&buf[len], start, str-start);
1705 len += str-start;
1706 break;
1707 }
1708 case '/':
1709 if(str[1] == '/')
1710 {
1711 size_t comment = strcspn(str, "\n\0");
1712 if (iscubepunct(str[2]))
1713 {
1714 memcpy(&buf[len], str, comment);
1715 len += comment;
1716 }
1717 str += comment;
1718 }
1719 else buf[len++] = *str++;
1720 break;
1721 case '@':
1722 case ']':
1723 if(str < end) { buf[len++] = *str++; break; }
1724 case '\0': goto done;
1725 }
1726 }
1727 done:
1728 memset(&buf[len], '\0', sizeof(uint)-len%sizeof(uint));
1729 code.advance(len/sizeof(uint)+1);
1730 code[start] |= len<<8;
1731 return true;
1732 }
1733
compileblocksub(vector<uint> & code,const char * & p,int prevargs)1734 static bool compileblocksub(vector<uint> &code, const char *&p, int prevargs)
1735 {
1736 stringslice lookup;
1737 switch(*p)
1738 {
1739 case '(':
1740 if(!compilearg(code, p, VAL_CANY, prevargs)) return false;
1741 break;
1742 case '[':
1743 if(!compilearg(code, p, VAL_CSTR, prevargs)) return false;
1744 code.add(CODE_LOOKUPMU);
1745 break;
1746 case '\"':
1747 cutstring(p, lookup);
1748 goto lookupid;
1749 default:
1750 {
1751 lookup.str = p;
1752 while(iscubealnum(*p) || *p=='_') p++;
1753 lookup.len = int(p-lookup.str);
1754 if(!lookup.len) return false;
1755 lookupid:
1756 ident *id = newident(lookup, IDF_UNKNOWN);
1757 if(id) switch(id->type)
1758 {
1759 case ID_VAR: code.add(CODE_IVAR|(id->index<<8)); goto done;
1760 case ID_FVAR: code.add(CODE_FVAR|(id->index<<8)); goto done;
1761 case ID_SVAR: code.add(CODE_SVARM|(id->index<<8)); goto done;
1762 case ID_ALIAS: code.add((id->index < MAXARGS ? CODE_LOOKUPMARG : CODE_LOOKUPM)|(id->index<<8)); goto done;
1763 }
1764 compilestr(code, lookup, true);
1765 code.add(CODE_LOOKUPMU);
1766 done:
1767 break;
1768 }
1769 }
1770 return true;
1771 }
1772
compileblockmain(vector<uint> & code,const char * & p,int wordtype,int prevargs)1773 static void compileblockmain(vector<uint> &code, const char *&p, int wordtype, int prevargs)
1774 {
1775 const char *line = p, *start = p;
1776 int concs = 0;
1777 for(int brak = 1; brak;)
1778 {
1779 p += strcspn(p, "@\"/[]\0");
1780 int c = *p++;
1781 switch(c)
1782 {
1783 case '\0':
1784 debugcodeline(line, "\frMissing \"]\"");
1785 p--;
1786 goto done;
1787 case '\"':
1788 p = parsestring(p);
1789 if(*p=='\"') p++;
1790 break;
1791 case '/':
1792 if(*p=='/') p += strcspn(p, "\n\0");
1793 break;
1794 case '[': brak++; break;
1795 case ']': brak--; break;
1796 case '@':
1797 {
1798 const char *esc = p;
1799 while(*p == '@') p++;
1800 int level = p - (esc - 1);
1801 if(brak > level) continue;
1802 else if(brak < level) debugcodeline(line, "\frToo many @s");
1803 if(!concs && prevargs >= MAXRESULTS) code.add(CODE_ENTER);
1804 if(concs + 2 > MAXARGS)
1805 {
1806 code.add(CODE_CONCW|RET_STR|(concs<<8));
1807 concs = 1;
1808 }
1809 if(compileblockstr(code, start, esc-1, true)) concs++;
1810 if(compileblocksub(code, p, prevargs + concs)) concs++;
1811 if(concs) start = p;
1812 else if(prevargs >= MAXRESULTS) code.pop();
1813 break;
1814 }
1815 }
1816 }
1817 done:
1818 if(p-1 > start)
1819 {
1820 if(!concs) switch(wordtype)
1821 {
1822 case VAL_POP:
1823 return;
1824 case VAL_CODE: case VAL_COND:
1825 p = compileblock(code, start, RET_NULL, ']');
1826 return;
1827 case VAL_IDENT:
1828 compileident(code, stringslice(start, p-1));
1829 return;
1830 }
1831 switch(wordtype)
1832 {
1833 case VAL_CSTR: case VAL_CODE: case VAL_IDENT: case VAL_CANY: case VAL_COND:
1834 compileblockstr(code, start, p-1, true);
1835 break;
1836 default:
1837 compileblockstr(code, start, p-1, concs > 0);
1838 break;
1839 }
1840 if(concs > 1) concs++;
1841 }
1842 if(concs)
1843 {
1844 if(prevargs >= MAXRESULTS)
1845 {
1846 code.add(CODE_CONCM|retcodeany(wordtype)|(concs<<8));
1847 code.add(CODE_EXIT|retcodeany(wordtype));
1848 }
1849 else code.add(CODE_CONCW|retcodeany(wordtype)|(concs<<8));
1850 }
1851 switch(wordtype)
1852 {
1853 case VAL_POP: if(concs || p-1 > start) code.add(CODE_POP); break;
1854 case VAL_COND: if(!concs && p-1 <= start) compilenull(code); else code.add(CODE_COND); break;
1855 case VAL_CODE: if(!concs && p-1 <= start) compileblock(code); else code.add(CODE_COMPILE); break;
1856 case VAL_IDENT: if(!concs && p-1 <= start) compileident(code); else code.add(CODE_IDENTU); break;
1857 case VAL_CSTR: case VAL_CANY:
1858 if(!concs && p-1 <= start) compilestr(code, NULL, 0, true);
1859 break;
1860 case VAL_STR: case VAL_NULL: case VAL_ANY: case VAL_WORD:
1861 if(!concs && p-1 <= start) compilestr(code);
1862 break;
1863 default:
1864 if(!concs)
1865 {
1866 if(p-1 <= start) compileval(code, wordtype);
1867 else code.add(CODE_FORCE|(wordtype<<CODE_RET));
1868 }
1869 break;
1870 }
1871 }
1872
compilearg(vector<uint> & code,const char * & p,int wordtype,int prevargs,stringslice & word)1873 static bool compilearg(vector<uint> &code, const char *&p, int wordtype, int prevargs, stringslice &word)
1874 {
1875 skipcomments(p);
1876 switch(*p)
1877 {
1878 case '\"':
1879 switch(wordtype)
1880 {
1881 case VAL_POP:
1882 p = parsestring(p+1);
1883 if(*p == '\"') p++;
1884 break;
1885 case VAL_COND:
1886 {
1887 char *s = cutstring(p);
1888 if(s[0]) compileblock(code, s);
1889 else compilenull(code);
1890 delete[] s;
1891 break;
1892 }
1893 case VAL_CODE:
1894 {
1895 char *s = cutstring(p);
1896 compileblock(code, s);
1897 delete[] s;
1898 break;
1899 }
1900 case VAL_WORD:
1901 cutstring(p, word);
1902 break;
1903 case VAL_ANY:
1904 case VAL_STR:
1905 compileunescapestring(code, p);
1906 break;
1907 case VAL_CANY:
1908 case VAL_CSTR:
1909 compileunescapestring(code, p, true);
1910 break;
1911 default:
1912 {
1913 stringslice s;
1914 cutstring(p, s);
1915 compileval(code, wordtype, s);
1916 break;
1917 }
1918 }
1919 return true;
1920 case '$': compilelookup(code, p, wordtype, prevargs); return true;
1921 case '(':
1922 p++;
1923 if(prevargs >= MAXRESULTS)
1924 {
1925 code.add(CODE_ENTER);
1926 compilestatements(code, p, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')');
1927 code.add(CODE_EXIT|retcodeany(wordtype));
1928 }
1929 else
1930 {
1931 int start = code.length();
1932 compilestatements(code, p, wordtype > VAL_ANY ? VAL_CANY : VAL_ANY, ')', prevargs);
1933 if(code.length() > start) code.add(CODE_RESULT_ARG|retcodeany(wordtype));
1934 else { compileval(code, wordtype); return true; }
1935 }
1936 switch(wordtype)
1937 {
1938 case VAL_POP: code.add(CODE_POP); break;
1939 case VAL_COND: code.add(CODE_COND); break;
1940 case VAL_CODE: code.add(CODE_COMPILE); break;
1941 case VAL_IDENT: code.add(CODE_IDENTU); break;
1942 }
1943 return true;
1944 case '[':
1945 p++;
1946 compileblockmain(code, p, wordtype, prevargs);
1947 return true;
1948 default:
1949 switch(wordtype)
1950 {
1951 case VAL_POP:
1952 {
1953 const char *s = p;
1954 p = parseword(p);
1955 return p != s;
1956 }
1957 case VAL_COND:
1958 {
1959 char *s = cutword(p);
1960 if(!s) return false;
1961 compileblock(code, s);
1962 delete[] s;
1963 return true;
1964 }
1965 case VAL_CODE:
1966 {
1967 char *s = cutword(p);
1968 if(!s) return false;
1969 compileblock(code, s);
1970 delete[] s;
1971 return true;
1972 }
1973 case VAL_WORD:
1974 cutword(p, word);
1975 return word.len!=0;
1976 default:
1977 {
1978 stringslice s;
1979 cutword(p, s);
1980 if(!s.len) return false;
1981 compileval(code, wordtype, s);
1982 return true;
1983 }
1984
1985 }
1986 }
1987 }
1988
compilestatements(vector<uint> & code,const char * & p,int rettype,int brak,int prevargs)1989 static void compilestatements(vector<uint> &code, const char *&p, int rettype, int brak, int prevargs)
1990 {
1991 const char *line = p;
1992 stringslice idname;
1993 int numargs;
1994 for(;;)
1995 {
1996 skipcomments(p);
1997 idname.str = NULL;
1998 bool more = compilearg(code, p, VAL_WORD, prevargs, idname);
1999 if(!more) goto endstatement;
2000 skipcomments(p);
2001 if(p[0] == '=') switch(p[1])
2002 {
2003 case '/':
2004 if(p[2] != '/') break;
2005 case ';': case ' ': case '\t': case '\r': case '\n': case '\0':
2006 p++;
2007 if(idname.str)
2008 {
2009 ident *id = newident(idname, IDF_UNKNOWN);
2010 if(id) switch(id->type)
2011 {
2012 case ID_ALIAS:
2013 if(!(more = compilearg(code, p, VAL_ANY, prevargs))) compilestr(code);
2014 code.add((id->index < MAXARGS ? CODE_ALIASARG : CODE_ALIAS)|(id->index<<8));
2015 goto endstatement;
2016 case ID_VAR:
2017 if(!(more = compilearg(code, p, VAL_INT, prevargs))) compileint(code);
2018 code.add(CODE_IVAR1|(id->index<<8));
2019 goto endstatement;
2020 case ID_FVAR:
2021 if(!(more = compilearg(code, p, VAL_FLOAT, prevargs))) compilefloat(code);
2022 code.add(CODE_FVAR1|(id->index<<8));
2023 goto endstatement;
2024 case ID_SVAR:
2025 if(!(more = compilearg(code, p, VAL_CSTR, prevargs))) compilestr(code);
2026 code.add(CODE_SVAR1|(id->index<<8));
2027 goto endstatement;
2028 }
2029 compilestr(code, idname, true);
2030 }
2031 if(!(more = compilearg(code, p, VAL_ANY))) compilestr(code);
2032 code.add(CODE_ALIASU);
2033 goto endstatement;
2034 }
2035 numargs = 0;
2036 if(!idname.str)
2037 {
2038 noid:
2039 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++;
2040 code.add(CODE_CALLU|(numargs<<8));
2041 }
2042 else
2043 {
2044 ident *id = idents.access(idname);
2045 if(!id || (id->flags&IDF_REWRITE && (!(identflags&IDF_WORLD) || !(id->flags&IDF_WORLD))))
2046 {
2047 if(!checknumber(idname)) { compilestr(code, idname, true); goto noid; }
2048 switch(rettype)
2049 {
2050 case VAL_ANY:
2051 case VAL_CANY:
2052 {
2053 char *end = (char *)idname.str;
2054 int val = int(strtoul(idname.str, &end, 0));
2055 if(end < idname.end()) compilestr(code, idname, rettype==VAL_CANY);
2056 else compileint(code, val);
2057 break;
2058 }
2059 default:
2060 compileval(code, rettype, idname);
2061 break;
2062 }
2063 code.add(CODE_RESULT);
2064 }
2065 else switch(id->type)
2066 {
2067 case ID_ALIAS:
2068 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_ANY, prevargs+numargs))) numargs++;
2069 code.add((id->index < MAXARGS ? CODE_CALLARG : CODE_CALL)|(numargs<<8)|(id->index<<13));
2070 break;
2071 case ID_COMMAND:
2072 {
2073 int comtype = CODE_COM, fakeargs = 0;
2074 bool rep = false;
2075 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
2076 {
2077 case 'S':
2078 case 's':
2079 if(more) more = compilearg(code, p, *fmt == 's' ? VAL_CSTR : VAL_STR, prevargs+numargs);
2080 if(!more)
2081 {
2082 if(rep) break;
2083 compilestr(code, NULL, 0, *fmt=='s');
2084 fakeargs++;
2085 }
2086 else if(!fmt[1])
2087 {
2088 int numconc = 1;
2089 while(numargs + numconc < MAXARGS && (more = compilearg(code, p, VAL_CSTR, prevargs+numargs+numconc))) numconc++;
2090 if(numconc > 1) code.add(CODE_CONC|RET_STR|(numconc<<8));
2091 }
2092 numargs++;
2093 break;
2094 case 'i': if(more) more = compilearg(code, p, VAL_INT, prevargs+numargs); if(!more) { if(rep) break; compileint(code); fakeargs++; } numargs++; break;
2095 case 'b': if(more) more = compilearg(code, p, VAL_INT, prevargs+numargs); if(!more) { if(rep) break; compileint(code, INT_MIN); fakeargs++; } numargs++; break;
2096 case 'f': if(more) more = compilearg(code, p, VAL_FLOAT, prevargs+numargs); if(!more) { if(rep) break; compilefloat(code); fakeargs++; } numargs++; break;
2097 case 'g': if(more) more = compilearg(code, p, VAL_FLOAT, prevargs+numargs); if(!more) { if(rep) break; compilefloat(code, -FLT_MAX); fakeargs++; } numargs++; break;
2098 case 'F': if(more) more = compilearg(code, p, VAL_FLOAT, prevargs+numargs); if(!more) { if(rep) break; code.add(CODE_DUP|RET_FLOAT); fakeargs++; } numargs++; break;
2099 case 'T':
2100 case 't': if(more) more = compilearg(code, p, *fmt == 't' ? VAL_CANY : VAL_ANY, prevargs+numargs); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
2101 case 'E': if(more) more = compilearg(code, p, VAL_COND, prevargs+numargs); if(!more) { if(rep) break; compilenull(code); fakeargs++; } numargs++; break;
2102 case 'e': if(more) more = compilearg(code, p, VAL_CODE, prevargs+numargs); if(!more) { if(rep) break; compileblock(code); fakeargs++; } numargs++; break;
2103 case 'r': if(more) more = compilearg(code, p, VAL_IDENT, prevargs+numargs); if(!more) { if(rep) break; compileident(code); fakeargs++; } numargs++; break;
2104 case '$': compileident(code, id); numargs++; break;
2105 case 'N': compileint(code, numargs-fakeargs); numargs++; break;
2106 #ifndef STANDALONE
2107 case 'D': comtype = CODE_COMD; numargs++; break;
2108 #endif
2109 case 'C': comtype = CODE_COMC; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++; goto compilecomv;
2110 case 'V': comtype = CODE_COMV; if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs))) numargs++; goto compilecomv;
2111 case '1': case '2': case '3': case '4':
2112 if(more && numargs < MAXARGS)
2113 {
2114 int numrep = *fmt-'0'+1;
2115 fmt -= numrep;
2116 rep = true;
2117 }
2118 else for(; numargs > MAXARGS; numargs--) code.add(CODE_POP);
2119 break;
2120 }
2121 code.add(comtype|retcodeany(rettype)|(id->index<<8));
2122 break;
2123 compilecomv:
2124 code.add(comtype|retcodeany(rettype)|(numargs<<8)|(id->index<<13));
2125 break;
2126 }
2127 case ID_LOCAL:
2128 if(more) while(numargs < MAXARGS && (more = compilearg(code, p, VAL_IDENT, prevargs+numargs))) numargs++;
2129 if(more) while((more = compilearg(code, p, VAL_POP)));
2130 code.add(CODE_LOCAL|(numargs<<8));
2131 break;
2132 case ID_DO:
2133 if(more) more = compilearg(code, p, VAL_CODE, prevargs);
2134 code.add((more ? CODE_DO : CODE_NULL) | retcodeany(rettype));
2135 break;
2136 case ID_DOARGS:
2137 if(more) more = compilearg(code, p, VAL_CODE, prevargs);
2138 code.add((more ? CODE_DOARGS : CODE_NULL) | retcodeany(rettype));
2139 break;
2140 case ID_IF:
2141 if(more) more = compilearg(code, p, VAL_CANY, prevargs);
2142 if(!more) code.add(CODE_NULL | retcodeany(rettype));
2143 else
2144 {
2145 int start1 = code.length();
2146 more = compilearg(code, p, VAL_CODE, prevargs+1);
2147 if(!more) { code.add(CODE_POP); code.add(CODE_NULL | retcodeany(rettype)); }
2148 else
2149 {
2150 int start2 = code.length();
2151 more = compilearg(code, p, VAL_CODE, prevargs+2);
2152 uint inst1 = code[start1], op1 = inst1&~CODE_RET_MASK, len1 = start2 - (start1+1);
2153 if(!more)
2154 {
2155 if(op1 == (CODE_BLOCK|(len1<<8)))
2156 {
2157 code[start1] = (len1<<8) | CODE_JUMP_FALSE;
2158 code[start1+1] = CODE_ENTER_RESULT;
2159 code[start1+len1] = (code[start1+len1]&~CODE_RET_MASK) | retcodeany(rettype);
2160 break;
2161 }
2162 compileblock(code);
2163 }
2164 else
2165 {
2166 uint inst2 = code[start2], op2 = inst2&~CODE_RET_MASK, len2 = code.length() - (start2+1);
2167 if(op2 == (CODE_BLOCK|(len2<<8)))
2168 {
2169 if(op1 == (CODE_BLOCK|(len1<<8)))
2170 {
2171 code[start1] = ((start2-start1)<<8) | CODE_JUMP_FALSE;
2172 code[start1+1] = CODE_ENTER_RESULT;
2173 code[start1+len1] = (code[start1+len1]&~CODE_RET_MASK) | retcodeany(rettype);
2174 code[start2] = (len2<<8) | CODE_JUMP;
2175 code[start2+1] = CODE_ENTER_RESULT;
2176 code[start2+len2] = (code[start2+len2]&~CODE_RET_MASK) | retcodeany(rettype);
2177 break;
2178 }
2179 else if(op1 == (CODE_EMPTY|(len1<<8)))
2180 {
2181 code[start1] = CODE_NULL | (inst2&CODE_RET_MASK);
2182 code[start2] = (len2<<8) | CODE_JUMP_TRUE;
2183 code[start2+1] = CODE_ENTER_RESULT;
2184 code[start2+len2] = (code[start2+len2]&~CODE_RET_MASK) | retcodeany(rettype);
2185 break;
2186 }
2187 }
2188 }
2189 code.add(CODE_COM|retcodeany(rettype)|(id->index<<8));
2190 }
2191 }
2192 break;
2193 case ID_RESULT:
2194 if(more) more = compilearg(code, p, VAL_ANY, prevargs);
2195 code.add((more ? CODE_RESULT : CODE_NULL) | retcodeany(rettype));
2196 break;
2197 case ID_NOT:
2198 if(more) more = compilearg(code, p, VAL_CANY, prevargs);
2199 code.add((more ? CODE_NOT : CODE_TRUE) | retcodeany(rettype));
2200 break;
2201 case ID_AND:
2202 case ID_OR:
2203 if(more) more = compilearg(code, p, VAL_COND, prevargs);
2204 if(!more) { code.add((id->type == ID_AND ? CODE_TRUE : CODE_FALSE) | retcodeany(rettype)); }
2205 else
2206 {
2207 numargs++;
2208 int start = code.length(), end = start;
2209 while(numargs < MAXARGS)
2210 {
2211 more = compilearg(code, p, VAL_COND, prevargs+numargs);
2212 if(!more) break;
2213 numargs++;
2214 if((code[end]&~CODE_RET_MASK) != (CODE_BLOCK|(uint(code.length()-(end+1))<<8))) break;
2215 end = code.length();
2216 }
2217 if(more)
2218 {
2219 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_COND, prevargs+numargs))) numargs++;
2220 code.add(CODE_COMV|retcodeany(rettype)|(numargs<<8)|(id->index<<13));
2221 }
2222 else
2223 {
2224 uint op = id->type == ID_AND ? CODE_JUMP_RESULT_FALSE : CODE_JUMP_RESULT_TRUE;
2225 code.add(op);
2226 end = code.length();
2227 while(start+1 < end)
2228 {
2229 uint len = code[start]>>8;
2230 code[start] = ((end-(start+1))<<8) | op;
2231 code[start+1] = CODE_ENTER;
2232 code[start+len] = (code[start+len]&~CODE_RET_MASK) | retcodeany(rettype);
2233 start += len+1;
2234 }
2235 }
2236 }
2237 break;
2238 case ID_VAR:
2239 if(!(more = compilearg(code, p, VAL_INT, prevargs))) code.add(CODE_PRINT|(id->index<<8));
2240 else if(!(id->flags&IDF_HEX) || !(more = compilearg(code, p, VAL_INT, prevargs+1))) code.add(CODE_IVAR1|(id->index<<8));
2241 else if(!(more = compilearg(code, p, VAL_INT, prevargs+2))) code.add(CODE_IVAR2|(id->index<<8));
2242 else code.add(CODE_IVAR3|(id->index<<8));
2243 break;
2244 case ID_FVAR:
2245 if(!(more = compilearg(code, p, VAL_FLOAT, prevargs))) code.add(CODE_PRINT|(id->index<<8));
2246 else code.add(CODE_FVAR1|(id->index<<8));
2247 break;
2248 case ID_SVAR:
2249 if(!(more = compilearg(code, p, VAL_CSTR, prevargs))) code.add(CODE_PRINT|(id->index<<8));
2250 else
2251 {
2252 do ++numargs;
2253 while(numargs < MAXARGS && (more = compilearg(code, p, VAL_CANY, prevargs+numargs)));
2254 if(numargs > 1) code.add(CODE_CONC|RET_STR|(numargs<<8));
2255 code.add(CODE_SVAR1|(id->index<<8));
2256 }
2257 break;
2258 }
2259 }
2260 endstatement:
2261 if(more) while(compilearg(code, p, VAL_POP));
2262 p += strcspn(p, ")];/\n\0");
2263 int c = *p++;
2264 switch(c)
2265 {
2266 case '\0':
2267 if(c != brak) debugcodeline(line, "\frMissing \"%c\"", brak);
2268 p--;
2269 return;
2270
2271 case ')':
2272 case ']':
2273 if(c == brak) return;
2274 debugcodeline(line, "\frUnexpected \"%c\"", c);
2275 break;
2276
2277 case '/':
2278 if(*p == '/') p += strcspn(p, "\n\0");
2279 goto endstatement;
2280 }
2281 }
2282 }
2283
compilemain(vector<uint> & code,const char * p,int rettype=VAL_ANY)2284 static void compilemain(vector<uint> &code, const char *p, int rettype = VAL_ANY)
2285 {
2286 code.add(CODE_START);
2287 compilestatements(code, p, VAL_ANY);
2288 code.add(CODE_EXIT|(rettype < VAL_ANY ? rettype<<CODE_RET : 0));
2289 }
2290
compilecode(const char * p)2291 uint *compilecode(const char *p)
2292 {
2293 vector<uint> buf;
2294 buf.reserve(64);
2295 compilemain(buf, p);
2296 uint *code = new uint[buf.length()];
2297 memcpy(code, buf.getbuf(), buf.length()*sizeof(uint));
2298 code[0] += 0x100;
2299 return code;
2300 }
2301
forcecode(tagval & v)2302 static inline const uint *forcecode(tagval &v)
2303 {
2304 if(v.type != VAL_CODE)
2305 {
2306 vector<uint> buf;
2307 buf.reserve(64);
2308 compilemain(buf, v.getstr());
2309 freearg(v);
2310 v.setcode(buf.disown()+1);
2311 }
2312 return v.code;
2313 }
2314
forcecond(tagval & v)2315 static inline void forcecond(tagval &v)
2316 {
2317 switch(v.type)
2318 {
2319 case VAL_STR: case VAL_MACRO: case VAL_CSTR:
2320 if(v.s[0]) forcecode(v);
2321 else v.setint(0);
2322 break;
2323 }
2324 }
2325
keepcode(uint * code)2326 void keepcode(uint *code)
2327 {
2328 if(!code) return;
2329 switch(*code&CODE_OP_MASK)
2330 {
2331 case CODE_START:
2332 *code += 0x100;
2333 return;
2334 }
2335 switch(code[-1]&CODE_OP_MASK)
2336 {
2337 case CODE_START:
2338 code[-1] += 0x100;
2339 break;
2340 case CODE_OFFSET:
2341 code -= int(code[-1]>>8);
2342 *code += 0x100;
2343 break;
2344 }
2345 }
2346
freecode(uint * code)2347 void freecode(uint *code)
2348 {
2349 if(!code) return;
2350 switch(*code&CODE_OP_MASK)
2351 {
2352 case CODE_START:
2353 *code -= 0x100;
2354 if(int(*code) < 0x100) delete[] code;
2355 return;
2356 }
2357 switch(code[-1]&CODE_OP_MASK)
2358 {
2359 case CODE_START:
2360 code[-1] -= 0x100;
2361 if(int(code[-1]) < 0x100) delete[] &code[-1];
2362 break;
2363 case CODE_OFFSET:
2364 code -= int(code[-1]>>8);
2365 *code -= 0x100;
2366 if(int(*code) < 0x100) delete[] code;
2367 break;
2368 }
2369 }
2370
printvar(ident * id,int n,const char * str)2371 void printvar(ident *id, int n, const char *str)
2372 {
2373 string output;
2374 if(id->flags&IDF_HEX && id->maxval == 0xFFFFFF)
2375 formatstring(output, "%s = 0x%.6X (%d, %d, %d)", id->name, n, (n>>16)&0xFF, (n>>8)&0xFF, n&0xFF);
2376 else if(id->flags&IDF_HEX && uint(id->maxval) == 0xFFFFFFFFU)
2377 formatstring(output, "%s = 0x%.8X (%d, %d, %d, %d)", id->name, n, n>>24, (n>>16)&0xFF, (n>>8)&0xFF, n&0xFF);
2378 else formatstring(output, id->flags&IDF_HEX ? "%s = 0x%X" : "%s = %d", id->name, n);
2379 if(str && *str) concformatstring(output, " (%s)", str);
2380 conoutft(CON_DEBUG, "%s", output);
2381 }
2382
printfvar(ident * id,float f,const char * str)2383 void printfvar(ident *id, float f, const char *str)
2384 {
2385 defformatstring(output, "%s = %s", id->name, floatstr(f));
2386 if(str && *str) concformatstring(output, " (%s)", str);
2387 conoutft(CON_DEBUG, "%s", output);
2388 }
2389
printsvar(ident * id,const char * s,const char * str)2390 void printsvar(ident *id, const char *s, const char *str)
2391 {
2392 defformatstring(output, strchr(s, '"') ? "%s = [%s]" : "%s = \"%s\"", id->name, s);
2393 if(str && *str) concformatstring(output, " (%s)", str);
2394 conoutft(CON_DEBUG, "%s", output);
2395 }
2396
printvar(ident * id)2397 void printvar(ident *id)
2398 {
2399 switch(id->type)
2400 {
2401 case ID_VAR: printvar(id, *id->storage.i); break;
2402 case ID_FVAR: printfvar(id, *id->storage.f); break;
2403 case ID_SVAR: printsvar(id, *id->storage.s); break;
2404 default: break;
2405 }
2406 }
2407
2408 typedef void (__cdecl *comfun)();
2409 typedef void (__cdecl *comfun1)(void *);
2410 typedef void (__cdecl *comfun2)(void *, void *);
2411 typedef void (__cdecl *comfun3)(void *, void *, void *);
2412 typedef void (__cdecl *comfun4)(void *, void *, void *, void *);
2413 typedef void (__cdecl *comfun5)(void *, void *, void *, void *, void *);
2414 typedef void (__cdecl *comfun6)(void *, void *, void *, void *, void *, void *);
2415 typedef void (__cdecl *comfun7)(void *, void *, void *, void *, void *, void *, void *);
2416 typedef void (__cdecl *comfun8)(void *, void *, void *, void *, void *, void *, void *, void *);
2417 typedef void (__cdecl *comfun9)(void *, void *, void *, void *, void *, void *, void *, void *, void *);
2418 typedef void (__cdecl *comfun10)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2419 typedef void (__cdecl *comfun11)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2420 typedef void (__cdecl *comfun12)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2421 typedef void (__cdecl *comfun13)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2422 typedef void (__cdecl *comfun14)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2423 typedef void (__cdecl *comfun15)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2424 typedef void (__cdecl *comfun16)(void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *, void *);
2425 typedef void (__cdecl *comfunv)(tagval *, int);
2426
skipcode(const uint * code,tagval & result=noret)2427 static const uint *skipcode(const uint *code, tagval &result = noret)
2428 {
2429 int depth = 0;
2430 for(;;)
2431 {
2432 uint op = *code++;
2433 switch(op&0xFF)
2434 {
2435 case CODE_MACRO:
2436 case CODE_VAL|RET_STR:
2437 {
2438 uint len = op>>8;
2439 code += len/sizeof(uint) + 1;
2440 continue;
2441 }
2442 case CODE_BLOCK:
2443 case CODE_JUMP:
2444 case CODE_JUMP_TRUE:
2445 case CODE_JUMP_FALSE:
2446 case CODE_JUMP_RESULT_TRUE:
2447 case CODE_JUMP_RESULT_FALSE:
2448 {
2449 uint len = op>>8;
2450 code += len;
2451 continue;
2452 }
2453 case CODE_ENTER:
2454 case CODE_ENTER_RESULT:
2455 ++depth;
2456 continue;
2457 case CODE_EXIT|RET_NULL: case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
2458 if(depth <= 0)
2459 {
2460 if(&result != &noret) forcearg(result, op&CODE_RET_MASK);
2461 return code;
2462 }
2463 --depth;
2464 continue;
2465 }
2466 }
2467 }
2468
2469 #ifndef STANDALONE
copycode(const uint * src)2470 static inline uint *copycode(const uint *src)
2471 {
2472 const uint *end = skipcode(src);
2473 size_t len = end - src;
2474 uint *dst = new uint[len + 1];
2475 *dst++ = CODE_START;
2476 memcpy(dst, src, len*sizeof(uint));
2477 return dst;
2478 }
2479
copyarg(tagval & dst,const tagval & src)2480 static inline void copyarg(tagval &dst, const tagval &src)
2481 {
2482 switch(src.type)
2483 {
2484 case VAL_INT:
2485 case VAL_FLOAT:
2486 case VAL_IDENT:
2487 dst = src;
2488 break;
2489 case VAL_STR:
2490 case VAL_MACRO:
2491 case VAL_CSTR:
2492 dst.setstr(newstring(src.s));
2493 break;
2494 case VAL_CODE:
2495 dst.setcode(copycode(src.code));
2496 break;
2497 default:
2498 dst.setnull();
2499 break;
2500 }
2501 }
2502
addreleaseaction(ident * id,tagval * args,int numargs)2503 static inline void addreleaseaction(ident *id, tagval *args, int numargs)
2504 {
2505 tagval *dst = addreleaseaction(id, numargs+1);
2506 if(dst) { args[numargs].setint(1); loopi(numargs+1) copyarg(dst[i], args[i]); }
2507 else args[numargs].setint(0);
2508 }
2509 #endif
2510
callcommand(ident * id,tagval * args,int numargs,bool lookup=false)2511 static inline void callcommand(ident *id, tagval *args, int numargs, bool lookup = false)
2512 {
2513 int i = -1, fakeargs = 0;
2514 bool rep = false;
2515 for(const char *fmt = id->args; *fmt; fmt++) switch(*fmt)
2516 {
2517 case 'i': if(++i >= numargs) { if(rep) break; args[i].setint(0); fakeargs++; } else forceint(args[i]); break;
2518 case 'b': if(++i >= numargs) { if(rep) break; args[i].setint(INT_MIN); fakeargs++; } else forceint(args[i]); break;
2519 case 'f': if(++i >= numargs) { if(rep) break; args[i].setfloat(0.0f); fakeargs++; } else forcefloat(args[i]); break;
2520 case 'g': if(++i >= numargs) { if(rep) break; args[i].setfloat(-FLT_MAX); fakeargs++; } else forcefloat(args[i]); break;
2521 case 'F': if(++i >= numargs) { if(rep) break; args[i].setfloat(args[i-1].getfloat()); fakeargs++; } else forcefloat(args[i]); break;
2522 case 'S': if(++i >= numargs) { if(rep) break; args[i].setstr(newstring("")); fakeargs++; } else forcestr(args[i]); break;
2523 case 's': if(++i >= numargs) { if(rep) break; args[i].setcstr(""); fakeargs++; } else forcestr(args[i]); break;
2524 case 'T':
2525 case 't': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } break;
2526 case 'E': if(++i >= numargs) { if(rep) break; args[i].setnull(); fakeargs++; } else forcecond(args[i]); break;
2527 case 'e':
2528 if(++i >= numargs)
2529 {
2530 if(rep) break;
2531 args[i].setcode(emptyblock[VAL_NULL]+1);
2532 fakeargs++;
2533 }
2534 else forcecode(args[i]);
2535 break;
2536 case 'r': if(++i >= numargs) { if(rep) break; args[i].setident(dummyident); fakeargs++; } else forceident(args[i]); break;
2537 case '$': if(++i < numargs) freearg(args[i]); args[i].setident(id); break;
2538 case 'N': if(++i < numargs) freearg(args[i]); args[i].setint(lookup ? -1 : i-fakeargs); break;
2539 #ifndef STANDALONE
2540 case 'D': if(++i < numargs) freearg(args[i]); addreleaseaction(id, args, i); fakeargs++; break;
2541 #endif
2542 case 'C': { i = max(i+1, numargs); vector<char> buf; ((comfun1)id->fun)(conc(buf, args, i, true)); goto cleanup; }
2543 case 'V': i = max(i+1, numargs); ((comfunv)id->fun)(args, i); goto cleanup;
2544 case '1': case '2': case '3': case '4': if(i+1 < numargs) { fmt -= *fmt-'0'+1; rep = true; } break;
2545 }
2546 ++i;
2547 #define OFFSETARG(n) n
2548 #define ARG(n) (id->argmask&(1<<(n)) ? (void *)args[OFFSETARG(n)].s : (void *)&args[OFFSETARG(n)].i)
2549 #define CALLCOM(n) \
2550 switch(n) \
2551 { \
2552 case 0: ((comfun)id->fun)(); break; \
2553 case 1: ((comfun1)id->fun)(ARG(0)); break; \
2554 case 2: ((comfun2)id->fun)(ARG(0), ARG(1)); break; \
2555 case 3: ((comfun3)id->fun)(ARG(0), ARG(1), ARG(2)); break; \
2556 case 4: ((comfun4)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3)); break; \
2557 case 5: ((comfun5)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4)); break; \
2558 case 6: ((comfun6)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5)); break; \
2559 case 7: ((comfun7)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6)); break; \
2560 case 8: ((comfun8)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7)); break; \
2561 case 9: ((comfun9)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8)); break; \
2562 case 10: ((comfun10)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9)); break; \
2563 case 11: ((comfun11)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10)); break; \
2564 case 12: ((comfun12)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11)); break; \
2565 case 13: ((comfun13)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11), ARG(12)); break; \
2566 case 14: ((comfun14)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11), ARG(12), ARG(13)); break; \
2567 case 15: ((comfun15)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11), ARG(12), ARG(13), ARG(14)); break; \
2568 case 16: ((comfun16)id->fun)(ARG(0), ARG(1), ARG(2), ARG(3), ARG(4), ARG(5), ARG(6), ARG(7), ARG(8), ARG(9), ARG(10), ARG(11), ARG(12), ARG(13), ARG(14), ARG(15)); break; \
2569 }
2570 CALLCOM(i)
2571 #undef OFFSETARG
2572 cleanup:
2573 loopk(i) freearg(args[k]);
2574 for(; i < numargs; i++) freearg(args[i]);
2575 }
2576
2577 #define MAXRUNDEPTH 255
2578 static int rundepth = 0;
2579
runcode(const uint * code,tagval & result)2580 static const uint *runcode(const uint *code, tagval &result)
2581 {
2582 result.setnull();
2583 if(rundepth >= MAXRUNDEPTH)
2584 {
2585 debugcode("Exceeded recursion limit");
2586 return skipcode(code, result);
2587 }
2588 ++rundepth;
2589 int numargs = 0;
2590 tagval args[MAXARGS+MAXRESULTS], *prevret = commandret;
2591 commandret = &result;
2592 for(;;)
2593 {
2594 uint op = *code++;
2595 switch(op&0xFF)
2596 {
2597 case CODE_START: case CODE_OFFSET: continue;
2598
2599 #define RETOP(op, val) \
2600 case op: \
2601 freearg(result); \
2602 val; \
2603 continue;
2604
2605 RETOP(CODE_NULL|RET_NULL, result.setnull())
2606 RETOP(CODE_NULL|RET_STR, result.setstr(newstring("")))
2607 RETOP(CODE_NULL|RET_INT, result.setint(0))
2608 RETOP(CODE_NULL|RET_FLOAT, result.setfloat(0.0f))
2609
2610 RETOP(CODE_FALSE|RET_STR, result.setstr(newstring("0")))
2611 case CODE_FALSE|RET_NULL:
2612 RETOP(CODE_FALSE|RET_INT, result.setint(0))
2613 RETOP(CODE_FALSE|RET_FLOAT, result.setfloat(0.0f))
2614
2615 RETOP(CODE_TRUE|RET_STR, result.setstr(newstring("1")))
2616 case CODE_TRUE|RET_NULL:
2617 RETOP(CODE_TRUE|RET_INT, result.setint(1))
2618 RETOP(CODE_TRUE|RET_FLOAT, result.setfloat(1.0f))
2619
2620 #define RETPOP(op, val) \
2621 RETOP(op, { --numargs; val; freearg(args[numargs]); })
2622
2623 RETPOP(CODE_NOT|RET_STR, result.setstr(newstring(getbool(args[numargs]) ? "0" : "1")))
2624 case CODE_NOT|RET_NULL:
2625 RETPOP(CODE_NOT|RET_INT, result.setint(getbool(args[numargs]) ? 0 : 1))
2626 RETPOP(CODE_NOT|RET_FLOAT, result.setfloat(getbool(args[numargs]) ? 0.0f : 1.0f))
2627
2628 case CODE_POP:
2629 freearg(args[--numargs]);
2630 continue;
2631 case CODE_ENTER:
2632 code = runcode(code, args[numargs++]);
2633 continue;
2634 case CODE_ENTER_RESULT:
2635 freearg(result);
2636 code = runcode(code, result);
2637 continue;
2638 case CODE_EXIT|RET_STR: case CODE_EXIT|RET_INT: case CODE_EXIT|RET_FLOAT:
2639 forcearg(result, op&CODE_RET_MASK);
2640 // fall-through
2641 case CODE_EXIT|RET_NULL:
2642 goto exit;
2643 case CODE_RESULT_ARG|RET_STR: case CODE_RESULT_ARG|RET_INT: case CODE_RESULT_ARG|RET_FLOAT:
2644 forcearg(result, op&CODE_RET_MASK);
2645 // fall-through
2646 case CODE_RESULT_ARG|RET_NULL:
2647 args[numargs++] = result;
2648 result.setnull();
2649 continue;
2650 case CODE_PRINT:
2651 printvar(identmap[op>>8]);
2652 continue;
2653 case CODE_LOCAL:
2654 {
2655 freearg(result);
2656 int numlocals = op>>8, offset = numargs-numlocals;
2657 identstack locals[MAXARGS];
2658 loopi(numlocals) pushalias(*args[offset+i].id, locals[i]);
2659 code = runcode(code, result);
2660 for(int i = offset; i < numargs; i++) popalias(*args[i].id);
2661 goto exit;
2662 }
2663
2664 case CODE_DOARGS|RET_NULL: case CODE_DOARGS|RET_STR: case CODE_DOARGS|RET_INT: case CODE_DOARGS|RET_FLOAT:
2665 {
2666 UNDOARGS
2667 freearg(result);
2668 runcode(args[--numargs].code, result);
2669 freearg(args[numargs]);
2670 forcearg(result, op&CODE_RET_MASK);
2671 REDOARGS
2672 continue;
2673 }
2674
2675 case CODE_DO|RET_NULL: case CODE_DO|RET_STR: case CODE_DO|RET_INT: case CODE_DO|RET_FLOAT:
2676 freearg(result);
2677 runcode(args[--numargs].code, result);
2678 freearg(args[numargs]);
2679 forcearg(result, op&CODE_RET_MASK);
2680 continue;
2681
2682 case CODE_JUMP:
2683 {
2684 uint len = op>>8;
2685 code += len;
2686 continue;
2687 }
2688 case CODE_JUMP_TRUE:
2689 {
2690 uint len = op>>8;
2691 if(getbool(args[--numargs])) code += len;
2692 freearg(args[numargs]);
2693 continue;
2694 }
2695 case CODE_JUMP_FALSE:
2696 {
2697 uint len = op>>8;
2698 if(!getbool(args[--numargs])) code += len;
2699 freearg(args[numargs]);
2700 continue;
2701 }
2702 case CODE_JUMP_RESULT_TRUE:
2703 {
2704 uint len = op>>8;
2705 freearg(result);
2706 --numargs;
2707 if(args[numargs].type == VAL_CODE) { runcode(args[numargs].code, result); freearg(args[numargs]); }
2708 else result = args[numargs];
2709 if(getbool(result)) code += len;
2710 continue;
2711 }
2712 case CODE_JUMP_RESULT_FALSE:
2713 {
2714 uint len = op>>8;
2715 freearg(result);
2716 --numargs;
2717 if(args[numargs].type == VAL_CODE) { runcode(args[numargs].code, result); freearg(args[numargs]); }
2718 else result = args[numargs];
2719 if(!getbool(result)) code += len;
2720 continue;
2721 }
2722
2723 case CODE_MACRO:
2724 {
2725 uint len = op>>8;
2726 args[numargs++].setmacro(code);
2727 code += len/sizeof(uint) + 1;
2728 continue;
2729 }
2730
2731 case CODE_VAL|RET_STR:
2732 {
2733 uint len = op>>8;
2734 args[numargs++].setstr(newstring((const char *)code, len));
2735 code += len/sizeof(uint) + 1;
2736 continue;
2737 }
2738 case CODE_VALI|RET_STR:
2739 {
2740 char s[4] = { char((op>>8)&0xFF), char((op>>16)&0xFF), char((op>>24)&0xFF), '\0' };
2741 args[numargs++].setstr(newstring(s));
2742 continue;
2743 }
2744 case CODE_VAL|RET_NULL:
2745 case CODE_VALI|RET_NULL: args[numargs++].setnull(); continue;
2746 case CODE_VAL|RET_INT: args[numargs++].setint(int(*code++)); continue;
2747 case CODE_VALI|RET_INT: args[numargs++].setint(int(op)>>8); continue;
2748 case CODE_VAL|RET_FLOAT: args[numargs++].setfloat(*(const float *)code++); continue;
2749 case CODE_VALI|RET_FLOAT: args[numargs++].setfloat(float(int(op)>>8)); continue;
2750
2751 case CODE_DUP|RET_NULL: args[numargs-1].getval(args[numargs]); numargs++; continue;
2752 case CODE_DUP|RET_INT: args[numargs].setint(args[numargs-1].getint()); numargs++; continue;
2753 case CODE_DUP|RET_FLOAT: args[numargs].setfloat(args[numargs-1].getfloat()); numargs++; continue;
2754 case CODE_DUP|RET_STR: args[numargs].setstr(newstring(args[numargs-1].getstr())); numargs++; continue;
2755
2756 case CODE_FORCE|RET_STR: forcestr(args[numargs-1]); continue;
2757 case CODE_FORCE|RET_INT: forceint(args[numargs-1]); continue;
2758 case CODE_FORCE|RET_FLOAT: forcefloat(args[numargs-1]); continue;
2759
2760 case CODE_RESULT|RET_NULL:
2761 freearg(result);
2762 result = args[--numargs];
2763 continue;
2764 case CODE_RESULT|RET_STR: case CODE_RESULT|RET_INT: case CODE_RESULT|RET_FLOAT:
2765 freearg(result);
2766 result = args[--numargs];
2767 forcearg(result, op&CODE_RET_MASK);
2768 continue;
2769
2770 case CODE_EMPTY|RET_NULL: args[numargs++].setcode(emptyblock[VAL_NULL]+1); break;
2771 case CODE_EMPTY|RET_STR: args[numargs++].setcode(emptyblock[VAL_STR]+1); break;
2772 case CODE_EMPTY|RET_INT: args[numargs++].setcode(emptyblock[VAL_INT]+1); break;
2773 case CODE_EMPTY|RET_FLOAT: args[numargs++].setcode(emptyblock[VAL_FLOAT]+1); break;
2774 case CODE_BLOCK:
2775 {
2776 uint len = op>>8;
2777 args[numargs++].setcode(code+1);
2778 code += len;
2779 continue;
2780 }
2781 case CODE_COMPILE:
2782 {
2783 tagval &arg = args[numargs-1];
2784 vector<uint> buf;
2785 switch(arg.type)
2786 {
2787 case VAL_INT: buf.reserve(8); buf.add(CODE_START); compileint(buf, arg.i); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2788 case VAL_FLOAT: buf.reserve(8); buf.add(CODE_START); compilefloat(buf, arg.f); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2789 case VAL_STR: case VAL_MACRO: case VAL_CSTR: buf.reserve(64); compilemain(buf, arg.s); freearg(arg); break;
2790 default: buf.reserve(8); buf.add(CODE_START); compilenull(buf); buf.add(CODE_RESULT); buf.add(CODE_EXIT); break;
2791 }
2792 arg.setcode(buf.disown()+1);
2793 continue;
2794 }
2795 case CODE_COND:
2796 {
2797 tagval &arg = args[numargs-1];
2798 switch(arg.type)
2799 {
2800 case VAL_STR: case VAL_MACRO: case VAL_CSTR:
2801 if(arg.s[0])
2802 {
2803 vector<uint> buf;
2804 buf.reserve(64);
2805 compilemain(buf, arg.s);
2806 freearg(arg);
2807 arg.setcode(buf.disown()+1);
2808 }
2809 else forcenull(arg);
2810 break;
2811 }
2812 continue;
2813 }
2814
2815 case CODE_IDENT:
2816 args[numargs++].setident(identmap[op>>8]);
2817 continue;
2818 case CODE_IDENTARG:
2819 {
2820 ident *id = identmap[op>>8];
2821 if(!(aliasstack->usedargs&(1<<id->index)))
2822 {
2823 pusharg(*id, nullval, aliasstack->argstack[id->index]);
2824 aliasstack->usedargs |= 1<<id->index;
2825 }
2826 args[numargs++].setident(id);
2827 continue;
2828 }
2829 case CODE_IDENTU:
2830 {
2831 tagval &arg = args[numargs-1];
2832 ident *id = arg.type == VAL_STR || arg.type == VAL_MACRO || arg.type == VAL_CSTR ? newident(arg.s, IDF_UNKNOWN) : dummyident;
2833 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index)))
2834 {
2835 pusharg(*id, nullval, aliasstack->argstack[id->index]);
2836 aliasstack->usedargs |= 1<<id->index;
2837 }
2838 freearg(arg);
2839 arg.setident(id);
2840 continue;
2841 }
2842
2843 case CODE_LOOKUPU|RET_STR:
2844 #define LOOKUPU(aval, sval, ival, fval, nval) { \
2845 tagval &arg = args[numargs-1]; \
2846 if(arg.type != VAL_STR && arg.type != VAL_MACRO && arg.type != VAL_CSTR) continue; \
2847 ident *id = idents.access(arg.s); \
2848 if(id) switch(id->type) \
2849 { \
2850 case ID_ALIAS: \
2851 if(id->flags&IDF_UNKNOWN) break; \
2852 freearg(arg); \
2853 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
2854 aval; \
2855 continue; \
2856 case ID_SVAR: freearg(arg); sval; continue; \
2857 case ID_VAR: freearg(arg); ival; continue; \
2858 case ID_FVAR: freearg(arg); fval; continue; \
2859 case ID_COMMAND: \
2860 { \
2861 freearg(arg); \
2862 arg.setnull(); \
2863 commandret = &arg; \
2864 tagval buf[MAXARGS]; \
2865 callcommand(id, buf, 0, true); \
2866 forcearg(arg, op&CODE_RET_MASK); \
2867 commandret = &result; \
2868 continue; \
2869 } \
2870 default: freearg(arg); nval; continue; \
2871 } \
2872 debugcode("\frUnknown alias lookup: %s", arg.s); \
2873 freearg(arg); \
2874 nval; \
2875 continue; \
2876 }
2877 LOOKUPU(arg.setstr(newstring(id->getstr())),
2878 arg.setstr(newstring(*id->storage.s)),
2879 arg.setstr(newstring(intstr(*id->storage.i))),
2880 arg.setstr(newstring(floatstr(*id->storage.f))),
2881 arg.setstr(newstring("")));
2882 case CODE_LOOKUP|RET_STR:
2883 #define LOOKUP(aval) { \
2884 ident *id = identmap[op>>8]; \
2885 if(id->flags&IDF_UNKNOWN) debugcode("\frUnknown alias lookup: %s", id->name); \
2886 aval; \
2887 continue; \
2888 }
2889 LOOKUP(args[numargs++].setstr(newstring(id->getstr())));
2890 case CODE_LOOKUPARG|RET_STR:
2891 #define LOOKUPARG(aval, nval) { \
2892 ident *id = identmap[op>>8]; \
2893 if(!(aliasstack->usedargs&(1<<id->index))) { nval; continue; } \
2894 aval; \
2895 continue; \
2896 }
2897 LOOKUPARG(args[numargs++].setstr(newstring(id->getstr())), args[numargs++].setstr(newstring("")));
2898 case CODE_LOOKUPU|RET_INT:
2899 LOOKUPU(arg.setint(id->getint()),
2900 arg.setint(parseint(*id->storage.s)),
2901 arg.setint(*id->storage.i),
2902 arg.setint(int(*id->storage.f)),
2903 arg.setint(0));
2904 case CODE_LOOKUP|RET_INT:
2905 LOOKUP(args[numargs++].setint(id->getint()));
2906 case CODE_LOOKUPARG|RET_INT:
2907 LOOKUPARG(args[numargs++].setint(id->getint()), args[numargs++].setint(0));
2908 case CODE_LOOKUPU|RET_FLOAT:
2909 LOOKUPU(arg.setfloat(id->getfloat()),
2910 arg.setfloat(parsefloat(*id->storage.s)),
2911 arg.setfloat(float(*id->storage.i)),
2912 arg.setfloat(*id->storage.f),
2913 arg.setfloat(0.0f));
2914 case CODE_LOOKUP|RET_FLOAT:
2915 LOOKUP(args[numargs++].setfloat(id->getfloat()));
2916 case CODE_LOOKUPARG|RET_FLOAT:
2917 LOOKUPARG(args[numargs++].setfloat(id->getfloat()), args[numargs++].setfloat(0.0f));
2918 case CODE_LOOKUPU|RET_NULL:
2919 LOOKUPU(id->getval(arg),
2920 arg.setstr(newstring(*id->storage.s)),
2921 arg.setint(*id->storage.i),
2922 arg.setfloat(*id->storage.f),
2923 arg.setnull());
2924 case CODE_LOOKUP|RET_NULL:
2925 LOOKUP(id->getval(args[numargs++]));
2926 case CODE_LOOKUPARG|RET_NULL:
2927 LOOKUPARG(id->getval(args[numargs++]), args[numargs++].setnull());
2928
2929 case CODE_LOOKUPMU|RET_STR:
2930 LOOKUPU(id->getcstr(arg),
2931 arg.setcstr(*id->storage.s),
2932 arg.setstr(newstring(intstr(*id->storage.i))),
2933 arg.setstr(newstring(floatstr(*id->storage.f))),
2934 arg.setcstr(""));
2935 case CODE_LOOKUPM|RET_STR:
2936 LOOKUP(id->getcstr(args[numargs++]));
2937 case CODE_LOOKUPMARG|RET_STR:
2938 LOOKUPARG(id->getcstr(args[numargs++]), args[numargs++].setcstr(""));
2939 case CODE_LOOKUPMU|RET_NULL:
2940 LOOKUPU(id->getcval(arg),
2941 arg.setcstr(*id->storage.s),
2942 arg.setint(*id->storage.i),
2943 arg.setfloat(*id->storage.f),
2944 arg.setnull());
2945 case CODE_LOOKUPM|RET_NULL:
2946 LOOKUP(id->getcval(args[numargs++]));
2947 case CODE_LOOKUPMARG|RET_NULL:
2948 LOOKUPARG(id->getcval(args[numargs++]), args[numargs++].setnull());
2949
2950 case CODE_SVAR|RET_STR: case CODE_SVAR|RET_NULL: args[numargs++].setstr(newstring(*identmap[op>>8]->storage.s)); continue;
2951 case CODE_SVAR|RET_INT: args[numargs++].setint(parseint(*identmap[op>>8]->storage.s)); continue;
2952 case CODE_SVAR|RET_FLOAT: args[numargs++].setfloat(parsefloat(*identmap[op>>8]->storage.s)); continue;
2953 case CODE_SVARM: args[numargs++].setcstr(*identmap[op>>8]->storage.s); continue;
2954 case CODE_SVAR1: setsvarchecked(identmap[op>>8], args[--numargs].s); freearg(args[numargs]); continue;
2955
2956 case CODE_IVAR|RET_INT: case CODE_IVAR|RET_NULL: args[numargs++].setint(*identmap[op>>8]->storage.i); continue;
2957 case CODE_IVAR|RET_STR: args[numargs++].setstr(newstring(intstr(*identmap[op>>8]->storage.i))); continue;
2958 case CODE_IVAR|RET_FLOAT: args[numargs++].setfloat(float(*identmap[op>>8]->storage.i)); continue;
2959 case CODE_IVAR1: setvarchecked(identmap[op>>8], args[--numargs].i); continue;
2960 case CODE_IVAR2: numargs -= 2; setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)); continue;
2961 case CODE_IVAR3: numargs -= 3; setvarchecked(identmap[op>>8], (args[numargs].i<<16)|(args[numargs+1].i<<8)|args[numargs+2].i); continue;
2962
2963 case CODE_FVAR|RET_FLOAT: case CODE_FVAR|RET_NULL: args[numargs++].setfloat(*identmap[op>>8]->storage.f); continue;
2964 case CODE_FVAR|RET_STR: args[numargs++].setstr(newstring(floatstr(*identmap[op>>8]->storage.f))); continue;
2965 case CODE_FVAR|RET_INT: args[numargs++].setint(int(*identmap[op>>8]->storage.f)); continue;
2966 case CODE_FVAR1: setfvarchecked(identmap[op>>8], args[--numargs].f); continue;
2967
2968 #define OFFSETARG(n) offset+n
2969 case CODE_COM|RET_NULL: case CODE_COM|RET_STR: case CODE_COM|RET_FLOAT: case CODE_COM|RET_INT:
2970 {
2971 ident *id = identmap[op>>8];
2972 int offset = numargs-id->numargs;
2973 forcenull(result);
2974 CALLCOM(id->numargs)
2975 forcearg(result, op&CODE_RET_MASK);
2976 freeargs(args, numargs, offset);
2977 continue;
2978 }
2979 #ifndef STANDALONE
2980 case CODE_COMD|RET_NULL: case CODE_COMD|RET_STR: case CODE_COMD|RET_FLOAT: case CODE_COMD|RET_INT:
2981 {
2982 ident *id = identmap[op>>8];
2983 int offset = numargs-(id->numargs-1);
2984 addreleaseaction(id, &args[offset], id->numargs-1);
2985 CALLCOM(id->numargs)
2986 forcearg(result, op&CODE_RET_MASK);
2987 freeargs(args, numargs, offset);
2988 continue;
2989 }
2990 #endif
2991 #undef OFFSETARG
2992
2993 case CODE_COMV|RET_NULL: case CODE_COMV|RET_STR: case CODE_COMV|RET_FLOAT: case CODE_COMV|RET_INT:
2994 {
2995 ident *id = identmap[op>>13];
2996 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
2997 forcenull(result);
2998 ((comfunv)id->fun)(&args[offset], callargs);
2999 forcearg(result, op&CODE_RET_MASK);
3000 freeargs(args, numargs, offset);
3001 continue;
3002 }
3003 case CODE_COMC|RET_NULL: case CODE_COMC|RET_STR: case CODE_COMC|RET_FLOAT: case CODE_COMC|RET_INT:
3004 {
3005 ident *id = identmap[op>>13];
3006 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
3007 forcenull(result);
3008 {
3009 vector<char> buf;
3010 buf.reserve(MAXSTRLEN);
3011 ((comfun1)id->fun)(conc(buf, &args[offset], callargs, true));
3012
3013 }
3014 forcearg(result, op&CODE_RET_MASK);
3015 freeargs(args, numargs, offset);
3016 continue;
3017 }
3018
3019 case CODE_CONC|RET_NULL: case CODE_CONC|RET_STR: case CODE_CONC|RET_FLOAT: case CODE_CONC|RET_INT:
3020 case CODE_CONCW|RET_NULL: case CODE_CONCW|RET_STR: case CODE_CONCW|RET_FLOAT: case CODE_CONCW|RET_INT:
3021 {
3022 int numconc = op>>8;
3023 char *s = conc(&args[numargs-numconc], numconc, (op&CODE_OP_MASK)==CODE_CONC);
3024 freeargs(args, numargs, numargs-numconc);
3025 args[numargs].setstr(s);
3026 forcearg(args[numargs], op&CODE_RET_MASK);
3027 numargs++;
3028 continue;
3029 }
3030
3031 case CODE_CONCM|RET_NULL: case CODE_CONCM|RET_STR: case CODE_CONCM|RET_FLOAT: case CODE_CONCM|RET_INT:
3032 {
3033 int numconc = op>>8;
3034 char *s = conc(&args[numargs-numconc], numconc, false);
3035 freeargs(args, numargs, numargs-numconc);
3036 result.setstr(s);
3037 forcearg(result, op&CODE_RET_MASK);
3038 continue;
3039 }
3040
3041 case CODE_ALIAS:
3042 setalias(*identmap[op>>8], args[--numargs], (identflags&IDF_WORLD)!=0);
3043 continue;
3044 case CODE_ALIASARG:
3045 setarg(*identmap[op>>8], args[--numargs]);
3046 continue;
3047 case CODE_ALIASU:
3048 numargs -= 2;
3049 setalias(args[numargs].getstr(), args[numargs+1], (identflags&IDF_WORLD)!=0);
3050 freearg(args[numargs]);
3051 continue;
3052
3053 #define SKIPARGS(offset) offset
3054 case CODE_CALL|RET_NULL: case CODE_CALL|RET_STR: case CODE_CALL|RET_FLOAT: case CODE_CALL|RET_INT:
3055 {
3056 #define FORCERESULT { \
3057 freeargs(args, numargs, SKIPARGS(offset)); \
3058 forcearg(result, op&CODE_RET_MASK); \
3059 continue; \
3060 }
3061 #define CALLALIAS { \
3062 identstack argstack[MAXARGS]; \
3063 for(int i = 0; i < callargs; i++) \
3064 pusharg(*identmap[i], args[offset + i], argstack[i]); \
3065 int oldargs = _numargs; \
3066 _numargs = callargs; \
3067 int oldflags = identflags; \
3068 identlink aliaslink = { id, aliasstack, (1<<callargs)-1, argstack }; \
3069 aliasstack = &aliaslink; \
3070 if(!id->code) id->code = compilecode(id->getstr()); \
3071 uint *code = id->code; \
3072 code[0] += 0x100; \
3073 runcode(code+1, result); \
3074 code[0] -= 0x100; \
3075 if(int(code[0]) < 0x100) delete[] code; \
3076 aliasstack = aliaslink.next; \
3077 identflags = oldflags; \
3078 for(int i = 0; i < callargs; i++) \
3079 poparg(*identmap[i]); \
3080 for(int argmask = aliaslink.usedargs&(~0U<<callargs), i = callargs; argmask; i++) \
3081 if(argmask&(1<<i)) { poparg(*identmap[i]); argmask &= ~(1<<i); } \
3082 forcearg(result, op&CODE_RET_MASK); \
3083 _numargs = oldargs; \
3084 numargs = SKIPARGS(offset); \
3085 }
3086 forcenull(result);
3087 ident *id = identmap[op>>13];
3088 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
3089 if(id->flags&IDF_UNKNOWN)
3090 {
3091 debugcode("\frUnknown command: %s", id->name);
3092 FORCERESULT;
3093 }
3094 CALLALIAS;
3095 continue;
3096 }
3097 case CODE_CALLARG|RET_NULL: case CODE_CALLARG|RET_STR: case CODE_CALLARG|RET_FLOAT: case CODE_CALLARG|RET_INT:
3098 {
3099 forcenull(result);
3100 ident *id = identmap[op>>13];
3101 int callargs = (op>>8)&0x1F, offset = numargs-callargs;
3102 if(!(aliasstack->usedargs&(1<<id->index))) FORCERESULT;
3103 CALLALIAS;
3104 continue;
3105 }
3106 #undef SKIPARGS
3107
3108 #define SKIPARGS(offset) offset-1
3109 case CODE_CALLU|RET_NULL: case CODE_CALLU|RET_STR: case CODE_CALLU|RET_FLOAT: case CODE_CALLU|RET_INT:
3110 {
3111 int callargs = op>>8, offset = numargs-callargs;
3112 tagval &idarg = args[offset-1];
3113 if(idarg.type != VAL_STR && idarg.type != VAL_MACRO && idarg.type != VAL_CSTR)
3114 {
3115 litval:
3116 freearg(result);
3117 result = idarg;
3118 forcearg(result, op&CODE_RET_MASK);
3119 while(--numargs >= offset) freearg(args[numargs]);
3120 continue;
3121 }
3122 ident *id = idents.access(idarg.s);
3123 bool idrewrite = !id || (id->flags&IDF_REWRITE && (!(identflags&IDF_WORLD) || !(id->flags&IDF_WORLD)));
3124 if(idrewrite)
3125 {
3126 noid:
3127 if(checknumber(idarg.s)) goto litval;
3128 if(offset == 1 && idrewrite && server::rewritecommand(id, args, numargs)) FORCERESULT;
3129 debugcode("\frUnknown command: %s", idarg.s);
3130 forcenull(result);
3131 FORCERESULT;
3132 }
3133 forcenull(result);
3134 switch(id->type)
3135 {
3136 default:
3137 if(!id->fun) FORCERESULT;
3138 // fall-through
3139 case ID_COMMAND:
3140 freearg(idarg);
3141 callcommand(id, &args[offset], callargs);
3142 forcearg(result, op&CODE_RET_MASK);
3143 numargs = offset - 1;
3144 continue;
3145 case ID_LOCAL:
3146 {
3147 identstack locals[MAXARGS];
3148 freearg(idarg);
3149 loopj(callargs) pushalias(*forceident(args[offset+j]), locals[j]);
3150 code = runcode(code, result);
3151 loopj(callargs) popalias(*args[offset+j].id);
3152 goto exit;
3153 }
3154 case ID_VAR:
3155 if(callargs <= 0) printvar(id); else setvarchecked(id, &args[offset], callargs);
3156 FORCERESULT;
3157 case ID_FVAR:
3158 if(callargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[offset]));
3159 FORCERESULT;
3160 case ID_SVAR:
3161 if(callargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[offset]));
3162 FORCERESULT;
3163 case ID_ALIAS:
3164 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) FORCERESULT;
3165 if(id->valtype==VAL_NULL) goto noid;
3166 freearg(idarg);
3167 CALLALIAS;
3168 continue;
3169 }
3170 }
3171 #undef SKIPARGS
3172 }
3173 }
3174 exit:
3175 commandret = prevret;
3176 --rundepth;
3177 return code;
3178 }
3179
executeret(const uint * code,tagval & result)3180 void executeret(const uint *code, tagval &result)
3181 {
3182 runcode(code, result);
3183 }
3184
executeret(const char * p,tagval & result)3185 void executeret(const char *p, tagval &result)
3186 {
3187 vector<uint> code;
3188 code.reserve(64);
3189 compilemain(code, p, VAL_ANY);
3190 runcode(code.getbuf()+1, result);
3191 if(int(code[0]) >= 0x100) code.disown();
3192 }
3193
executeret(ident * id,tagval * args,int numargs,bool lookup,tagval & result)3194 void executeret(ident *id, tagval *args, int numargs, bool lookup, tagval &result)
3195 {
3196 result.setnull();
3197 ++rundepth;
3198 tagval *prevret = commandret;
3199 commandret = &result;
3200 if(rundepth > MAXRUNDEPTH) debugcode("Exceeded recursion limit");
3201 else if(id) switch(id->type)
3202 {
3203 default:
3204 if(!id->fun) break;
3205 // fall-through
3206 case ID_COMMAND:
3207 if(numargs < id->numargs)
3208 {
3209 tagval buf[MAXARGS];
3210 memcpy(buf, args, numargs*sizeof(tagval));
3211 callcommand(id, buf, numargs, lookup);
3212 }
3213 else callcommand(id, args, numargs, lookup);
3214 numargs = 0;
3215 break;
3216 case ID_VAR:
3217 if(numargs <= 0) printvar(id); else setvarchecked(id, args, numargs);
3218 break;
3219 case ID_FVAR:
3220 if(numargs <= 0) printvar(id); else setfvarchecked(id, forcefloat(args[0]));
3221 break;
3222 case ID_SVAR:
3223 if(numargs <= 0) printvar(id); else setsvarchecked(id, forcestr(args[0]));
3224 break;
3225 case ID_ALIAS:
3226 if(id->index < MAXARGS && !(aliasstack->usedargs&(1<<id->index))) break;
3227 if(id->valtype==VAL_NULL) break;
3228 #define callargs numargs
3229 #define offset 0
3230 #define op RET_NULL
3231 #define SKIPARGS(offset) offset
3232 CALLALIAS;
3233 #undef callargs
3234 #undef offset
3235 #undef op
3236 #undef SKIPARGS
3237 break;
3238 }
3239 freeargs(args, numargs, 0);
3240 commandret = prevret;
3241 --rundepth;
3242 }
3243
executestr(const uint * code)3244 char *executestr(const uint *code)
3245 {
3246 tagval result;
3247 runcode(code, result);
3248 if(result.type == VAL_NULL) return NULL;
3249 forcestr(result);
3250 return result.s;
3251 }
3252
executestr(const char * p)3253 char *executestr(const char *p)
3254 {
3255 tagval result;
3256 executeret(p, result);
3257 if(result.type == VAL_NULL) return NULL;
3258 forcestr(result);
3259 return result.s;
3260 }
3261
executestr(ident * id,tagval * args,int numargs,bool lookup)3262 char *executestr(ident *id, tagval *args, int numargs, bool lookup)
3263 {
3264 tagval result;
3265 executeret(id, args, numargs, lookup, result);
3266 if(result.type == VAL_NULL) return NULL;
3267 forcestr(result);
3268 return result.s;
3269 }
3270
execidentstr(const char * name,bool lookup)3271 char *execidentstr(const char *name, bool lookup)
3272 {
3273 ident *id = idents.access(name);
3274 return id ? executestr(id, NULL, 0, lookup) : NULL;
3275 }
3276
execute(const uint * code)3277 int execute(const uint *code)
3278 {
3279 tagval result;
3280 runcode(code, result);
3281 int i = result.getint();
3282 freearg(result);
3283 return i;
3284 }
3285
execute(const char * p)3286 int execute(const char *p)
3287 {
3288 vector<uint> code;
3289 code.reserve(64);
3290 compilemain(code, p, VAL_INT);
3291 tagval result;
3292 runcode(code.getbuf()+1, result);
3293 if(int(code[0]) >= 0x100) code.disown();
3294 int i = result.getint();
3295 freearg(result);
3296 return i;
3297 }
3298
execute(const char * p,bool nonworld)3299 int execute(const char *p, bool nonworld)
3300 {
3301 int oldflags = identflags;
3302 if(nonworld) identflags &= ~IDF_WORLD;
3303 int result = execute(p);
3304 if(nonworld) identflags = oldflags;
3305 return result;
3306 }
3307
execute(ident * id,tagval * args,int numargs,bool lookup)3308 int execute(ident *id, tagval *args, int numargs, bool lookup)
3309 {
3310 tagval result;
3311 executeret(id, args, numargs, lookup, result);
3312 int i = result.getint();
3313 freearg(result);
3314 return i;
3315 }
3316
execident(const char * name,int noid,bool lookup)3317 int execident(const char *name, int noid, bool lookup)
3318 {
3319 ident *id = idents.access(name);
3320 return id ? execute(id, NULL, 0, lookup) : noid;
3321 }
3322
executefloat(const uint * code)3323 float executefloat(const uint *code)
3324 {
3325 tagval result;
3326 runcode(code, result);
3327 float f = result.getfloat();
3328 freearg(result);
3329 return f;
3330 }
3331
executefloat(const char * p)3332 float executefloat(const char *p)
3333 {
3334 tagval result;
3335 executeret(p, result);
3336 float f = result.getfloat();
3337 freearg(result);
3338 return f;
3339 }
3340
executefloat(ident * id,tagval * args,int numargs,bool lookup)3341 float executefloat(ident *id, tagval *args, int numargs, bool lookup)
3342 {
3343 tagval result;
3344 executeret(id, args, numargs, lookup, result);
3345 float f = result.getfloat();
3346 freearg(result);
3347 return f;
3348 }
3349
execidentfloat(const char * name,float noid,bool lookup)3350 float execidentfloat(const char *name, float noid, bool lookup)
3351 {
3352 ident *id = idents.access(name);
3353 return id ? executefloat(id, NULL, 0, lookup) : noid;
3354 }
3355
executebool(const uint * code)3356 bool executebool(const uint *code)
3357 {
3358 tagval result;
3359 runcode(code, result);
3360 bool b = getbool(result);
3361 freearg(result);
3362 return b;
3363 }
3364
executebool(const char * p)3365 bool executebool(const char *p)
3366 {
3367 tagval result;
3368 executeret(p, result);
3369 bool b = getbool(result);
3370 freearg(result);
3371 return b;
3372 }
3373
executebool(ident * id,tagval * args,int numargs,bool lookup)3374 bool executebool(ident *id, tagval *args, int numargs, bool lookup)
3375 {
3376 tagval result;
3377 executeret(id, args, numargs, lookup, result);
3378 bool b = getbool(result);
3379 freearg(result);
3380 return b;
3381 }
3382
execidentbool(const char * name,bool noid,bool lookup)3383 bool execidentbool(const char *name, bool noid, bool lookup)
3384 {
3385 ident *id = idents.access(name);
3386 return id ? executebool(id, NULL, 0, lookup) : noid;
3387 }
3388
execfile(const char * cfgfile,bool msg,int flags)3389 bool execfile(const char *cfgfile, bool msg, int flags)
3390 {
3391 string s;
3392 copystring(s, cfgfile);
3393 char *buf = loadfile(s, NULL);
3394 if(!buf)
3395 {
3396 if(msg || verbose >= 2) conoutf("\frCould not read %s", cfgfile);
3397 return false;
3398 }
3399 int oldflags = identflags, oldversion = versioning;
3400 if(flags&EXEC_NOWORLD) identflags &= ~IDF_WORLD;
3401 if(flags&EXEC_VERSION) versioning = flags&EXEC_BUILTIN ? 2 : 1;
3402 const char *oldsourcefile = sourcefile, *oldsourcestr = sourcestr;
3403 sourcefile = cfgfile;
3404 sourcestr = buf;
3405 execute(buf);
3406 sourcefile = oldsourcefile;
3407 sourcestr = oldsourcestr;
3408 if(flags&EXEC_NOWORLD) identflags = oldflags;
3409 if(flags&EXEC_VERSION) versioning = oldversion;
3410 delete[] buf;
3411 if(verbose >= 2) conoutf("\faLoaded script %s", cfgfile);
3412 return true;
3413 }
3414 ICOMMAND(0, exec, "sib", (char *file, int *flags, int *msg), intret(execfile(file, *msg != 0, *flags) ? 1 : 0));
3415
escapestring(const char * s)3416 const char *escapestring(const char *s)
3417 {
3418 stridx = (stridx + 1)%4;
3419 vector<char> &buf = strbuf[stridx];
3420 buf.setsize(0);
3421 buf.add('"');
3422 for(; *s; s++) switch(*s)
3423 {
3424 case '\n': buf.put("^n", 2); break;
3425 case '\t': buf.put("^t", 2); break;
3426 case '\f': buf.put("^f", 2); break;
3427 case '"': buf.put("^\"", 2); break;
3428 case '^': buf.put("^^", 2); break;
3429 default: buf.add(*s); break;
3430 }
3431 buf.put("\"\0", 2);
3432 return buf.getbuf();
3433 }
3434
3435 ICOMMAND(0, escape, "s", (char *s), result(escapestring(s)));
3436 ICOMMAND(0, unescape, "s", (char *s),
3437 {
3438 int len = strlen(s);
3439 char *d = newstring(len);
3440 unescapestring(d, s, &s[len]);
3441 stringret(d);
3442 });
3443
escapeid(const char * s)3444 const char *escapeid(const char *s)
3445 {
3446 const char *end = s + strcspn(s, "\"/;()[]@ \f\t\r\n\0");
3447 return *end ? escapestring(s) : s;
3448 }
3449
validateblock(const char * s)3450 bool validateblock(const char *s)
3451 {
3452 const int maxbrak = 100;
3453 static char brakstack[maxbrak];
3454 int brakdepth = 0;
3455 for(; *s; s++) switch(*s)
3456 {
3457 case '[': case '(': if(brakdepth >= maxbrak) return false; brakstack[brakdepth++] = *s; break;
3458 case ']': if(brakdepth <= 0 || brakstack[--brakdepth] != '[') return false; break;
3459 case ')': if(brakdepth <= 0 || brakstack[--brakdepth] != '(') return false; break;
3460 case '"': s = parsestring(s + 1); if(*s != '"') return false; break;
3461 case '/': if(s[1] == '/') return false; break;
3462 case '@': case '\f': return false;
3463 }
3464 return brakdepth == 0;
3465 }
3466
changedvars()3467 void changedvars()
3468 {
3469 vector<ident *> ids;
3470 enumerate(idents, ident, id, if(id.flags&IDF_WORLD) ids.add(&id));
3471 ids.sortname();
3472 loopv(ids) printvar(ids[i]);
3473 }
3474 COMMAND(0, changedvars, "");
3475
3476 // below the commands that implement a small imperative language. thanks to the semantics of
3477 // () and [] expressions, any control construct can be defined trivially.
3478
3479 static string retbuf[4];
3480 static int retidx = 0;
3481
intstr(int v)3482 const char *intstr(int v)
3483 {
3484 retidx = (retidx + 1)%4;
3485 intformat(retbuf[retidx], v);
3486 return retbuf[retidx];
3487 }
3488
intstr(ident * id)3489 const char *intstr(ident *id)
3490 {
3491 retidx = (retidx + 1)%4;
3492 formatstring(retbuf[retidx], id->flags&IDF_HEX && *id->storage.i >= 0 ? (id->maxval==0xFFFFFF ? "0x%.6X" : (uint(id->maxval)==0xFFFFFFFFU ? "0x%.8X" : "0x%X")) : "%d", id->flags&IDF_HEX && uint(id->maxval)==0xFFFFFFFFU ? uint(*id->storage.i) : *id->storage.i);
3493 return retbuf[retidx];
3494 }
3495
intret(int v)3496 void intret(int v)
3497 {
3498 commandret->setint(v);
3499 }
3500
floatstr(float v)3501 const char *floatstr(float v)
3502 {
3503 retidx = (retidx + 1)%4;
3504 floatformat(retbuf[retidx], v);
3505 return retbuf[retidx];
3506 }
3507
floatret(float v)3508 void floatret(float v)
3509 {
3510 commandret->setfloat(v);
3511 }
3512
numberstr(double v)3513 const char *numberstr(double v)
3514 {
3515 retidx = (retidx + 1)%4;
3516 numberformat(retbuf[retidx], v);
3517 return retbuf[retidx];
3518 }
3519
numberret(double v)3520 void numberret(double v)
3521 {
3522 int i = int(v);
3523 if(v == i) commandret->setint(i);
3524 else commandret->setfloat(v);
3525 }
3526
3527 #undef ICOMMANDNAME
3528 #define ICOMMANDNAME(name) _stdcmd
3529 #undef ICOMMANDSNAME
3530 #define ICOMMANDSNAME _stdcmd
3531
3532 ICOMMANDK(0, do, ID_DO, "e", (uint *body), executeret(body, *commandret));
3533
doargs(uint * body)3534 static void doargs(uint *body)
3535 {
3536 if(aliasstack != &noalias)
3537 {
3538 UNDOARGS
3539 executeret(body, *commandret);
3540 REDOARGS
3541 }
3542 else executeret(body, *commandret);
3543 }
3544 COMMANDK(0, doargs, ID_DOARGS, "e");
3545
3546 ICOMMANDK(0, if, ID_IF, "tee", (tagval *cond, uint *t, uint *f), executeret(getbool(*cond) ? t : f, *commandret));
3547 ICOMMAND(0, ?, "tTT", (tagval *cond, tagval *t, tagval *f), result(*(getbool(*cond) ? t : f)));
3548
3549 ICOMMAND(0, pushif, "rTe", (ident *id, tagval *v, uint *code),
3550 {
3551 if(id->type != ID_ALIAS || id->index < MAXARGS) return;
3552 if(getbool(*v))
3553 {
3554 identstack stack;
3555 pusharg(*id, *v, stack);
3556 v->type = VAL_NULL;
3557 id->flags &= ~IDF_UNKNOWN;
3558 executeret(code, *commandret);
3559 poparg(*id);
3560 }
3561 });
3562
loopiter(ident * id,identstack & stack,const tagval & v)3563 void loopiter(ident *id, identstack &stack, const tagval &v)
3564 {
3565 if(id->stack != &stack)
3566 {
3567 pusharg(*id, v, stack);
3568 id->flags &= ~IDF_UNKNOWN;
3569 }
3570 else
3571 {
3572 if(id->valtype == VAL_STR) delete[] id->val.s;
3573 cleancode(*id);
3574 id->setval(v);
3575 }
3576 }
3577
loopend(ident * id,identstack & stack)3578 void loopend(ident *id, identstack &stack)
3579 {
3580 if(id->stack == &stack) poparg(*id);
3581 }
3582
setiter(ident & id,int i,identstack & stack)3583 static inline void setiter(ident &id, int i, identstack &stack)
3584 {
3585 if(id.stack == &stack)
3586 {
3587 if(id.valtype != VAL_INT)
3588 {
3589 if(id.valtype == VAL_STR) delete[] id.val.s;
3590 cleancode(id);
3591 id.valtype = VAL_INT;
3592 }
3593 id.val.i = i;
3594 }
3595 else
3596 {
3597 tagval t;
3598 t.setint(i);
3599 pusharg(id, t, stack);
3600 id.flags &= ~IDF_UNKNOWN;
3601 }
3602 }
3603
doloop(ident & id,int offset,int n,int step,bool rev,uint * body)3604 static inline void doloop(ident &id, int offset, int n, int step, bool rev, uint *body)
3605 {
3606 if(n <= 0 || id.type != ID_ALIAS) return;
3607 identstack stack;
3608 if(rev)
3609 {
3610 loopirev(n)
3611 {
3612 setiter(id, offset + i*step, stack);
3613 execute(body);
3614 }
3615 }
3616 else
3617 {
3618 loopi(n)
3619 {
3620 setiter(id, offset + i*step, stack);
3621 execute(body);
3622 }
3623 }
3624 poparg(id);
3625 }
3626
3627 ICOMMAND(0, loop, "rie", (ident *id, int *n, uint *body), doloop(*id, 0, *n, 1, false, body));
3628 ICOMMAND(0, loop+, "riie", (ident *id, int *offset, int *n, uint *body), doloop(*id, *offset, *n, 1, false, body));
3629 ICOMMAND(0, loop*, "riie", (ident *id, int *step, int *n, uint *body), doloop(*id, 0, *n, *step, false, body));
3630 ICOMMAND(0, loop+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), doloop(*id, *offset, *n, *step, false, body));
3631 ICOMMAND(0, looprev, "rie", (ident *id, int *n, uint *body), doloop(*id, 0, *n, 1, true, body));
3632 ICOMMAND(0, looprev+, "riie", (ident *id, int *offset, int *n, uint *body), doloop(*id, *offset, *n, 1, true, body));
3633 ICOMMAND(0, looprev*, "riie", (ident *id, int *step, int *n, uint *body), doloop(*id, 0, *n, *step, true, body));
3634 ICOMMAND(0, looprev+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), doloop(*id, *offset, *n, *step, true, body));
3635
loopwhile(ident & id,int offset,int n,int step,bool rev,uint * cond,uint * body)3636 static inline void loopwhile(ident &id, int offset, int n, int step, bool rev, uint *cond, uint *body)
3637 {
3638 if(n <= 0 || id.type!=ID_ALIAS) return;
3639 identstack stack;
3640 if(rev)
3641 {
3642 loopirev(n)
3643 {
3644 setiter(id, offset + i*step, stack);
3645 if(!executebool(cond)) break;
3646 execute(body);
3647 }
3648 }
3649 else
3650 {
3651 loopi(n)
3652 {
3653 setiter(id, offset + i*step, stack);
3654 if(!executebool(cond)) break;
3655 execute(body);
3656 }
3657 }
3658 poparg(id);
3659 }
3660 ICOMMAND(0, loopwhile, "riee", (ident *id, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, 1, false, cond, body));
3661 ICOMMAND(0, loopwhile+, "riiee", (ident *id, int *offset, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, 1, false, cond, body));
3662 ICOMMAND(0, loopwhile*, "riiee", (ident *id, int *step, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, *step, false, cond, body));
3663 ICOMMAND(0, loopwhile+*, "riiiee", (ident *id, int *offset, int *step, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, *step, false, cond, body));
3664 ICOMMAND(0, loopwhilerev, "riee", (ident *id, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, 1, true, cond, body));
3665 ICOMMAND(0, loopwhilerev+, "riiee", (ident *id, int *offset, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, 1, true, cond, body));
3666 ICOMMAND(0, loopwhilerev*, "riiee", (ident *id, int *step, int *n, uint *cond, uint *body), loopwhile(*id, 0, *n, *step, true, cond, body));
3667 ICOMMAND(0, loopwhilerev+*, "riiiee", (ident *id, int *offset, int *step, int *n, uint *cond, uint *body), loopwhile(*id, *offset, *n, *step, true, cond, body));
3668
3669 ICOMMAND(0, while, "ee", (uint *cond, uint *body), while(executebool(cond)) execute(body));
3670
loopconc(ident & id,int offset,int n,int step,bool rev,uint * body,bool space)3671 static inline void loopconc(ident &id, int offset, int n, int step, bool rev, uint *body, bool space)
3672 {
3673 if(n <= 0 || id.type != ID_ALIAS) return;
3674 identstack stack;
3675 vector<char> s;
3676 if(rev)
3677 {
3678 loopirev(n)
3679 {
3680 setiter(id, offset + i*step, stack);
3681 tagval v;
3682 executeret(body, v);
3683 const char *vstr = v.getstr();
3684 int len = strlen(vstr);
3685 if(space && i) s.add(' ');
3686 s.put(vstr, len);
3687 freearg(v);
3688 }
3689 }
3690 else
3691 {
3692 loopi(n)
3693 {
3694 setiter(id, offset + i*step, stack);
3695 tagval v;
3696 executeret(body, v);
3697 const char *vstr = v.getstr();
3698 int len = strlen(vstr);
3699 if(space && i) s.add(' ');
3700 s.put(vstr, len);
3701 freearg(v);
3702 }
3703 }
3704 if(n > 0) poparg(id);
3705 s.add('\0');
3706 commandret->setstr(s.disown());
3707 }
3708 ICOMMAND(0, loopconcat, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, false, body, true));
3709 ICOMMAND(0, loopconcat+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, false, body, true));
3710 ICOMMAND(0, loopconcat*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, false, body, true));
3711 ICOMMAND(0, loopconcat+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, false, body, true));
3712 ICOMMAND(0, loopconcatword, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, false, body, false));
3713 ICOMMAND(0, loopconcatword+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, false, body, false));
3714 ICOMMAND(0, loopconcatword*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, false, body, false));
3715 ICOMMAND(0, loopconcatword+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, false, body, false));
3716 ICOMMAND(0, loopconcatrev, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, true, body, true));
3717 ICOMMAND(0, loopconcatrev+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, true, body, true));
3718 ICOMMAND(0, loopconcatrev*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, true, body, true));
3719 ICOMMAND(0, loopconcatrev+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, true, body, true));
3720 ICOMMAND(0, loopconcatwordrev, "rie", (ident *id, int *n, uint *body), loopconc(*id, 0, *n, 1, true, body, false));
3721 ICOMMAND(0, loopconcatwordrev+, "riie", (ident *id, int *offset, int *n, uint *body), loopconc(*id, *offset, *n, 1, true, body, false));
3722 ICOMMAND(0, loopconcatwordrev*, "riie", (ident *id, int *step, int *n, uint *body), loopconc(*id, 0, *n, *step, true, body, false));
3723 ICOMMAND(0, loopconcatwordrev+*, "riiie", (ident *id, int *offset, int *step, int *n, uint *body), loopconc(*id, *offset, *n, *step, true, body, false));
3724
concat(tagval * v,int n)3725 void concat(tagval *v, int n)
3726 {
3727 commandret->setstr(conc(v, n, true));
3728 }
3729 COMMAND(0, concat, "V");
3730
concatword(tagval * v,int n)3731 void concatword(tagval *v, int n)
3732 {
3733 commandret->setstr(conc(v, n, false));
3734 }
3735 COMMAND(0, concatword, "V");
3736
append(ident * id,tagval * v,bool space)3737 void append(ident *id, tagval *v, bool space)
3738 {
3739 if(id->type != ID_ALIAS || v->type == VAL_NULL) return;
3740 tagval r;
3741 const char *prefix = id->getstr();
3742 if(prefix[0]) r.setstr(conc(v, 1, space, prefix));
3743 else v->getval(r);
3744 if(id->index < MAXARGS) setarg(*id, r);
3745 else setalias(*id, r, (identflags&IDF_WORLD)!=0);
3746 }
3747 ICOMMAND(0, append, "rt", (ident *id, tagval *v), append(id, v, true));
3748 ICOMMAND(0, appendword, "rt", (ident *id, tagval *v), append(id, v, false));
3749
result(tagval & v)3750 void result(tagval &v)
3751 {
3752 *commandret = v;
3753 v.type = VAL_NULL;
3754 }
3755
stringret(char * s)3756 void stringret(char *s)
3757 {
3758 commandret->setstr(s);
3759 }
3760
result(const char * s)3761 void result(const char *s)
3762 {
3763 commandret->setstr(newstring(s));
3764 }
3765
3766 ICOMMANDK(0, result, ID_RESULT, "T", (tagval *v),
3767 {
3768 *commandret = *v;
3769 v->type = VAL_NULL;
3770 });
3771
format(tagval * args,int numargs)3772 void format(tagval *args, int numargs)
3773 {
3774 vector<char> s;
3775 const char *f = args[0].getstr();
3776 while(*f)
3777 {
3778 int c = *f++;
3779 if(c == '%')
3780 {
3781 int i = *f++;
3782 if(i >= '1' && i <= '9')
3783 {
3784 i -= '0';
3785 const char *sub = i < numargs ? args[i].getstr() : "";
3786 while(*sub) s.add(*sub++);
3787 }
3788 else s.add(i);
3789 }
3790 else s.add(c);
3791 }
3792 s.add('\0');
3793 commandret->setstr(s.disown());
3794 }
3795 COMMAND(0, format, "V");
3796
3797 static const char *liststart = NULL, *listend = NULL, *listquotestart = NULL, *listquoteend = NULL;
3798
skiplist(const char * & p)3799 static inline void skiplist(const char *&p)
3800 {
3801 for(;;)
3802 {
3803 p += strspn(p, " \t\r\n");
3804 if(p[0]!='/' || p[1]!='/') break;
3805 p += strcspn(p, "\n\0");
3806 }
3807 }
3808
parselist(const char * & s,const char * & start=liststart,const char * & end=listend,const char * & quotestart=listquotestart,const char * & quoteend=listquoteend)3809 static bool parselist(const char *&s, const char *&start = liststart, const char *&end = listend, const char *"estart = listquotestart, const char *"eend = listquoteend)
3810 {
3811 skiplist(s);
3812 switch(*s)
3813 {
3814 case '"': quotestart = s++; start = s; s = parsestring(s); end = s; if(*s == '"') s++; quoteend = s; break;
3815 case '(': case '[':
3816 quotestart = s;
3817 start = s+1;
3818 for(int braktype = *s++, brak = 1;;)
3819 {
3820 s += strcspn(s, "\"/;()[]\0");
3821 int c = *s++;
3822 switch(c)
3823 {
3824 case '\0': s--; quoteend = end = s; return true;
3825 case '"': s = parsestring(s); if(*s == '"') s++; break;
3826 case '/': if(*s == '/') s += strcspn(s, "\n\0"); break;
3827 case '(': case '[': if(c == braktype) brak++; break;
3828 case ')': if(braktype == '(' && --brak <= 0) goto endblock; break;
3829 case ']': if(braktype == '[' && --brak <= 0) goto endblock; break;
3830 }
3831 }
3832 endblock:
3833 end = s-1;
3834 quoteend = s;
3835 break;
3836 case '\0': case ')': case ']': return false;
3837 default: quotestart = start = s; s = parseword(s); quoteend = end = s; break;
3838 }
3839 skiplist(s);
3840 if(*s == ';') s++;
3841 return true;
3842 }
3843
listelem(const char * start=liststart,const char * end=listend,const char * quotestart=listquotestart)3844 static inline char *listelem(const char *start = liststart, const char *end = listend, const char *quotestart = listquotestart)
3845 {
3846 size_t len = end-start;
3847 char *s = newstring(len);
3848 if(*quotestart == '"') unescapestring(s, start, end);
3849 else { memcpy(s, start, len); s[len] = '\0'; }
3850 return s;
3851 }
3852
explodelist(const char * s,vector<char * > & elems,int limit)3853 void explodelist(const char *s, vector<char *> &elems, int limit)
3854 {
3855 const char *start, *end, *qstart;
3856 while((limit < 0 || elems.length() < limit) && parselist(s, start, end, qstart))
3857 elems.add(listelem(start, end, qstart));
3858 }
3859
indexlist(const char * s,int pos)3860 char *indexlist(const char *s, int pos)
3861 {
3862 loopi(pos) if(!parselist(s)) return newstring("");
3863 const char *start, *end, *qstart;
3864 return parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring("");
3865 }
3866
listlen(const char * s)3867 int listlen(const char *s)
3868 {
3869 int n = 0;
3870 while(parselist(s)) n++;
3871 return n;
3872 }
3873 ICOMMAND(0, listlen, "s", (char *s), intret(listlen(s)));
3874
indexlist(const char * s,int pos,int & len)3875 const char *indexlist(const char *s, int pos, int &len)
3876 {
3877 loopi(pos) if(!parselist(s)) break;
3878 const char *start = s, *end = s;
3879 parselist(s, start, end);
3880 len = end-start;
3881 return start;
3882 }
3883
at(tagval * args,int numargs)3884 void at(tagval *args, int numargs)
3885 {
3886 if(!numargs) return;
3887 const char *start = args[0].getstr(), *end = start + strlen(start), *qstart = "";
3888 for(int i = 1; i < numargs; i++)
3889 {
3890 const char *list = start;
3891 int pos = args[i].getint();
3892 if(pos < 0) pos = listlen(list)+pos;
3893 for(; pos > 0; pos--) if(!parselist(list)) break;
3894 if(pos > 0 || !parselist(list, start, end, qstart)) start = end = qstart = "";
3895 }
3896 commandret->setstr(listelem(start, end, qstart));
3897 }
3898 COMMAND(0, at, "si1V");
3899
substr(char * s,int * start,int * count,int * numargs)3900 void substr(char *s, int *start, int *count, int *numargs)
3901 {
3902 int len = strlen(s), offset = clamp(*start, 0, len);
3903 commandret->setstr(newstring(&s[offset], *numargs >= 3 ? clamp(*count, 0, len - offset) : len - offset));
3904 }
3905 COMMAND(0, substr, "siiN");
3906
sublist(const char * s,int * skip,int * count,int * numargs)3907 void sublist(const char *s, int *skip, int *count, int *numargs)
3908 {
3909 int offset = max(*skip, 0), len = *numargs >= 3 ? max(*count, 0) : -1;
3910 loopi(offset) if(!parselist(s)) break;
3911 if(len < 0) { if(offset > 0) skiplist(s); commandret->setstr(newstring(s)); return; }
3912 const char *list = s, *start, *end, *qstart, *qend = s;
3913 if(len > 0 && parselist(s, start, end, list, qend)) while(--len > 0 && parselist(s, start, end, qstart, qend));
3914 commandret->setstr(newstring(list, qend - list));
3915 }
3916 COMMAND(0, sublist, "siiN");
3917
3918 ICOMMAND(0, stripcolors, "s", (char *s),
3919 {
3920 size_t len = strlen(s);
3921 char *d = newstring(len);
3922 filterstring(d, s, false, true, true, false, len);
3923 stringret(d);
3924 });
3925
setiter(ident & id,char * val,identstack & stack)3926 static inline void setiter(ident &id, char *val, identstack &stack)
3927 {
3928 if(id.stack == &stack)
3929 {
3930 if(id.valtype == VAL_STR) delete[] id.val.s;
3931 else id.valtype = VAL_STR;
3932 cleancode(id);
3933 id.val.s = val;
3934 }
3935 else
3936 {
3937 tagval t;
3938 t.setstr(val);
3939 pusharg(id, t, stack);
3940 id.flags &= ~IDF_UNKNOWN;
3941 }
3942 }
3943
listfind(ident * id,const char * list,const uint * body)3944 void listfind(ident *id, const char *list, const uint *body)
3945 {
3946 if(id->type!=ID_ALIAS) { intret(-1); return; }
3947 identstack stack;
3948 int n = -1;
3949 for(const char *s = list, *start, *end; parselist(s, start, end);)
3950 {
3951 ++n;
3952 setiter(*id, newstring(start, end-start), stack);
3953 if(executebool(body)) { intret(n); goto found; }
3954 }
3955 intret(-1);
3956 found:
3957 if(n >= 0) poparg(*id);
3958 }
3959 COMMAND(0, listfind, "rse");
3960
listassoc(ident * id,const char * list,const uint * body)3961 void listassoc(ident *id, const char *list, const uint *body)
3962 {
3963 if(id->type!=ID_ALIAS) return;
3964 identstack stack;
3965 int n = -1;
3966 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);)
3967 {
3968 ++n;
3969 setiter(*id, newstring(start, end-start), stack);
3970 if(executebool(body)) { if(parselist(s, start, end, qstart)) stringret(listelem(start, end, qstart)); break; }
3971 if(!parselist(s)) break;
3972 }
3973 if(n >= 0) poparg(*id);
3974 }
3975 COMMAND(0, listassoc, "rse");
3976
3977 #define LISTFIND(name, fmt, type, init, cmp) \
3978 ICOMMAND(0, name, "s" fmt "i", (char *list, type *val, int *skip), \
3979 { \
3980 int n = 0; \
3981 init; \
3982 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++) \
3983 { \
3984 if(cmp) { intret(n); return; } \
3985 loopi(*skip) { if(!parselist(s)) goto notfound; n++; } \
3986 } \
3987 notfound: \
3988 intret(-1); \
3989 });
3990 LISTFIND(listfind=, "i", int, , parseint(start) == *val);
3991 LISTFIND(listfind=f, "f", float, , parsefloat(start) == *val);
3992 LISTFIND(listfind=s, "s", char, int len = (int)strlen(val), int(end-start) == len && !memcmp(start, val, len));
3993
3994 #define LISTASSOC(name, fmt, type, init, cmp) \
3995 ICOMMAND(0, name, "s" fmt, (char *list, type *val), \
3996 { \
3997 init; \
3998 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);) \
3999 { \
4000 if(cmp) { if(parselist(s, start, end, qstart)) stringret(listelem(start, end, qstart)); return; } \
4001 if(!parselist(s)) break; \
4002 } \
4003 });
4004 LISTASSOC(listassoc=, "i", int, , parseint(start) == *val);
4005 LISTASSOC(listassoc=f, "f", float, , parsefloat(start) == *val);
4006 LISTASSOC(listassoc=s, "s", char, int len = (int)strlen(val), int(end-start) == len && !memcmp(start, val, len));
4007
looplist(ident * id,const char * list,const uint * body)4008 void looplist(ident *id, const char *list, const uint *body)
4009 {
4010 if(id->type!=ID_ALIAS) return;
4011 identstack stack;
4012 int n = 0;
4013 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
4014 {
4015 setiter(*id, listelem(start, end, qstart), stack);
4016 execute(body);
4017 }
4018 if(n) poparg(*id);
4019 }
4020 COMMAND(0, looplist, "rse");
4021
looplist2(ident * id,ident * id2,const char * list,const uint * body)4022 void looplist2(ident *id, ident *id2, const char *list, const uint *body)
4023 {
4024 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS) return;
4025 identstack stack, stack2;
4026 int n = 0;
4027 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 2)
4028 {
4029 setiter(*id, listelem(start, end, qstart), stack);
4030 setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
4031 execute(body);
4032 }
4033 if(n) { poparg(*id); poparg(*id2); }
4034 }
4035 COMMAND(0, looplist2, "rrse");
4036
looplist3(ident * id,ident * id2,ident * id3,const char * list,const uint * body)4037 void looplist3(ident *id, ident *id2, ident *id3, const char *list, const uint *body)
4038 {
4039 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS || id3->type!=ID_ALIAS) return;
4040 identstack stack, stack2, stack3;
4041 int n = 0;
4042 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 3)
4043 {
4044 setiter(*id, listelem(start, end, qstart), stack);
4045 setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
4046 setiter(*id3, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack3);
4047 execute(body);
4048 }
4049 if(n) { poparg(*id); poparg(*id2); poparg(*id3); }
4050 }
4051 COMMAND(0, looplist3, "rrrse");
4052
looplist4(ident * id,ident * id2,ident * id3,ident * id4,const char * list,const uint * body)4053 void looplist4(ident *id, ident *id2, ident *id3, ident *id4, const char *list, const uint *body)
4054 {
4055 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS || id3->type!=ID_ALIAS || id4->type!=ID_ALIAS) return;
4056 identstack stack, stack2, stack3, stack4;
4057 int n = 0, r = 0;
4058 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 3)
4059 {
4060 setiter(*id, listelem(start, end, qstart), stack);
4061 setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
4062 setiter(*id3, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack3);
4063 setiter(*id4, r, stack4);
4064 execute(body);
4065 }
4066 if(n) { poparg(*id); poparg(*id2); poparg(*id3); poparg(*id4); }
4067 }
4068 COMMAND(0, looplist4, "rrrrse");
4069
looplistn(ident * id,ident * id2,const char * list,const uint * body)4070 void looplistn(ident *id, ident *id2, const char *list, const uint *body)
4071 {
4072 if(id->type!=ID_ALIAS || id2->type!=ID_ALIAS) return;
4073 identstack stack, stack2;
4074 int n = 0;
4075 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
4076 {
4077 setiter(*id, listelem(start, end, qstart), stack);
4078 setiter(*id2, n, stack2);
4079 execute(body);
4080 }
4081 if(n) { poparg(*id); poparg(*id2); }
4082 }
4083 COMMAND(0, looplistn, "rrse");
4084
looplistconc(ident * id,const char * list,const uint * body,bool space)4085 void looplistconc(ident *id, const char *list, const uint *body, bool space)
4086 {
4087 if(id->type!=ID_ALIAS) return;
4088 identstack stack;
4089 vector<char> r;
4090 int n = 0;
4091 for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
4092 {
4093 char *val = listelem(start, end, qstart);
4094 setiter(*id, val, stack);
4095
4096 if(n && space) r.add(' ');
4097
4098 tagval v;
4099 executeret(body, v);
4100 const char *vstr = v.getstr();
4101 int len = strlen(vstr);
4102 r.put(vstr, len);
4103 freearg(v);
4104 }
4105 if(n) poparg(*id);
4106 r.add('\0');
4107 commandret->setstr(r.disown());
4108 }
4109 ICOMMAND(0, looplistconcat, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, true));
4110 ICOMMAND(0, looplistconcatword, "rse", (ident *id, char *list, uint *body), looplistconc(id, list, body, false));
4111
listfilter(ident * id,const char * list,const uint * body)4112 void listfilter(ident *id, const char *list, const uint *body)
4113 {
4114 if(id->type!=ID_ALIAS) return;
4115 identstack stack;
4116 vector<char> r;
4117 int n = 0;
4118 for(const char *s = list, *start, *end, *qstart, *qend; parselist(s, start, end, qstart, qend); n++)
4119 {
4120 char *val = newstring(start, end-start);
4121 setiter(*id, val, stack);
4122
4123 if(executebool(body))
4124 {
4125 if(r.length()) r.add(' ');
4126 r.put(qstart, qend-qstart);
4127 }
4128 }
4129 if(n) poparg(*id);
4130 r.add('\0');
4131 commandret->setstr(r.disown());
4132 }
4133 COMMAND(0, listfilter, "rse");
4134
listcount(ident * id,const char * list,const uint * body)4135 void listcount(ident *id, const char *list, const uint *body)
4136 {
4137 if(id->type!=ID_ALIAS) return;
4138 identstack stack;
4139 int n = 0, r = 0;
4140 for(const char *s = list, *start, *end; parselist(s, start, end); n++)
4141 {
4142 char *val = newstring(start, end-start);
4143 setiter(*id, val, stack);
4144 if(executebool(body)) r++;
4145 }
4146 if(n) poparg(*id);
4147 intret(r);
4148 }
4149 COMMAND(0, listcount, "rse");
4150
prettylist(const char * s,const char * conj)4151 void prettylist(const char *s, const char *conj)
4152 {
4153 vector<char> p;
4154 const char *start, *end, *qstart;
4155 for(int len = listlen(s), n = 0; parselist(s, start, end, qstart); n++)
4156 {
4157 if(*qstart == '"') p.advance(unescapestring(p.reserve(end - start + 1).buf, start, end));
4158 else p.put(start, end - start);
4159 if(n+1 < len)
4160 {
4161 if(len > 2 || !conj[0]) p.add(',');
4162 if(n+2 == len && conj[0])
4163 {
4164 p.add(' ');
4165 p.put(conj, strlen(conj));
4166 }
4167 p.add(' ');
4168 }
4169 }
4170 p.add('\0');
4171 commandret->setstr(p.disown());
4172 }
4173 COMMAND(0, prettylist, "ss");
4174
listincludes(const char * list,const char * needle,int needlelen)4175 int listincludes(const char *list, const char *needle, int needlelen)
4176 {
4177 int offset = 0;
4178 for(const char *s = list, *start, *end; parselist(s, start, end);)
4179 {
4180 int len = end - start;
4181 if(needlelen == len && !strncmp(needle, start, len)) return offset;
4182 offset++;
4183 }
4184 return -1;
4185 }
4186 ICOMMAND(0, indexof, "ss", (char *list, char *elem), intret(listincludes(list, elem, strlen(elem))));
4187
shrinklist(const char * list,const char * limit,int failover,bool invert)4188 char *shrinklist(const char *list, const char *limit, int failover, bool invert)
4189 {
4190 vector<char> p;
4191 for(const char *s = list, *start, *end, *qstart, *qend; parselist(s, start, end, qstart, qend);)
4192 {
4193 int inlist = listincludes(limit, start, end-start);
4194 if(invert ? inlist < 0 : inlist >= 0)
4195 {
4196 if(!p.empty()) p.add(' ');
4197 p.put(qstart, qend-qstart);
4198 }
4199 }
4200 if(failover && p.empty())
4201 {
4202 const char *all = "";
4203 switch(failover)
4204 {
4205 case 2: all = limit; break;
4206 case 1: default: all = list; break;
4207 }
4208 return newstring(all);
4209 }
4210 p.add('\0');
4211 return newstring(p.getbuf(), p.length()-1);
4212 }
4213 ICOMMAND(0, shrinklist, "ssii", (char *s, char *t, int *n, int *v), commandret->setstr(shrinklist(s, t, *n, *v!=0)));
4214
4215 #define LISTMERGECMD(name, init, iter, filter, dir) \
4216 ICOMMAND(0, name, "ss", (const char *list, const char *elems), \
4217 { \
4218 vector<char> p; \
4219 init; \
4220 for(const char *start, *end, *qstart, *qend; parselist(iter, start, end, qstart, qend);) \
4221 { \
4222 int len = end - start; \
4223 if(listincludes(filter, start, len) dir 0) \
4224 { \
4225 if(!p.empty()) p.add(' '); \
4226 p.put(qstart, qend-qstart); \
4227 } \
4228 } \
4229 p.add('\0'); \
4230 commandret->setstr(p.disown()); \
4231 })
4232
4233 LISTMERGECMD(listdel, , list, elems, <);
4234 LISTMERGECMD(listintersect, , list, elems, >=);
4235 LISTMERGECMD(listunion, p.put(list, strlen(list)), elems, list, <);
4236
listsplice(const char * s,const char * vals,int * skip,int * count)4237 void listsplice(const char *s, const char *vals, int *skip, int *count)
4238 {
4239 int offset = max(*skip, 0), len = max(*count, 0);
4240 const char *list = s, *start, *end, *qstart, *qend = s;
4241 loopi(offset) if(!parselist(s, start, end, qstart, qend)) break;
4242 vector<char> p;
4243 if(qend > list) p.put(list, qend-list);
4244 if(*vals)
4245 {
4246 if(!p.empty()) p.add(' ');
4247 p.put(vals, strlen(vals));
4248 }
4249 loopi(len) if(!parselist(s)) break;
4250 skiplist(s);
4251 switch(*s)
4252 {
4253 case '\0': case ')': case ']': break;
4254 default:
4255 if(!p.empty()) p.add(' ');
4256 p.put(s, strlen(s));
4257 break;
4258 }
4259 p.add('\0');
4260 commandret->setstr(p.disown());
4261 }
4262 COMMAND(0, listsplice, "ssii");
4263
4264 ICOMMAND(0, listfiles, "ss", (char *dir, char *ext),
4265 {
4266 vector<char *> files;
4267 listfiles(dir, ext[0] ? ext : NULL, files);
4268
4269 vector<char> p;
4270 loopv(files)
4271 {
4272 if(i) p.add(' ');
4273
4274 const char *str = escapestring(files[i]);
4275 while(*str) p.add(*(str++));
4276 }
4277 p.add('\0');
4278
4279 commandret->setstr(p.disown());
4280 });
4281
4282 ICOMMAND(0, loopfiles, "rsse", (ident *id, char *dir, char *ext, uint *body),
4283 {
4284 if(id->type!=ID_ALIAS) return;
4285 identstack stack;
4286 vector<char *> files;
4287 listfiles(dir, ext[0] ? ext : NULL, files);
4288 files.sort();
4289 files.uniquedeletearrays();
4290 loopv(files)
4291 {
4292 setiter(*id, files[i], stack);
4293 execute(body);
4294 }
4295 if(files.length()) poparg(*id);
4296 });
4297
findfile_(char * name)4298 void findfile_(char *name)
4299 {
4300 string fname;
4301 copystring(fname, name);
4302 path(fname);
4303 intret(
4304 #ifndef STANDALONE
4305 findzipfile(fname) ||
4306 #endif
4307 fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0
4308 );
4309 }
4310 COMMANDN(0, findfile, findfile_, "s");
4311
4312 struct sortitem
4313 {
4314 const char *str, *quotestart, *quoteend;
4315
quotelengthsortitem4316 int quotelength() const { return int(quoteend-quotestart); }
4317 };
4318
4319 struct sortfun
4320 {
4321 ident *x, *y;
4322 uint *body;
4323
operator ()sortfun4324 bool operator()(const sortitem &xval, const sortitem &yval)
4325 {
4326 if(x->valtype != VAL_CSTR) x->valtype = VAL_CSTR;
4327 cleancode(*x);
4328 x->val.code = (const uint *)xval.str;
4329 if(y->valtype != VAL_CSTR) y->valtype = VAL_CSTR;
4330 cleancode(*y);
4331 y->val.code = (const uint *)yval.str;
4332 return executebool(body);
4333 }
4334 };
4335
sortlist(char * list,ident * x,ident * y,uint * body,uint * unique)4336 void sortlist(char *list, ident *x, ident *y, uint *body, uint *unique)
4337 {
4338 if(x == y || x->type != ID_ALIAS || y->type != ID_ALIAS) return;
4339
4340 vector<sortitem> items;
4341 int clen = strlen(list), total = 0;
4342 char *cstr = newstring(list, clen);
4343 const char *curlist = list, *start, *end, *quotestart, *quoteend;
4344 while(parselist(curlist, start, end, quotestart, quoteend))
4345 {
4346 cstr[end - list] = '\0';
4347 sortitem item = { &cstr[start - list], quotestart, quoteend };
4348 items.add(item);
4349 total += item.quotelength();
4350 }
4351
4352 if(items.empty())
4353 {
4354 commandret->setstr(cstr);
4355 return;
4356 }
4357
4358 identstack xstack, ystack;
4359 pusharg(*x, nullval, xstack); x->flags &= ~IDF_UNKNOWN;
4360 pusharg(*y, nullval, ystack); y->flags &= ~IDF_UNKNOWN;
4361
4362 int totalunique = total, numunique = items.length();
4363 if(body)
4364 {
4365 sortfun f = { x, y, body };
4366 items.sort(f);
4367 if((*unique&CODE_OP_MASK) != CODE_EXIT)
4368 {
4369 f.body = unique;
4370 totalunique = items[0].quotelength();
4371 numunique = 1;
4372 for(int i = 1; i < items.length(); i++)
4373 {
4374 sortitem &item = items[i];
4375 if(f(items[i-1], item)) item.quotestart = NULL;
4376 else { totalunique += item.quotelength(); numunique++; }
4377 }
4378 }
4379 }
4380 else
4381 {
4382 sortfun f = { x, y, unique };
4383 totalunique = items[0].quotelength();
4384 numunique = 1;
4385 for(int i = 1; i < items.length(); i++)
4386 {
4387 sortitem &item = items[i];
4388 loopj(i)
4389 {
4390 sortitem &prev = items[j];
4391 if(prev.quotestart && f(item, prev)) { item.quotestart = NULL; break; }
4392 }
4393 if(item.quotestart) { totalunique += item.quotelength(); numunique++; }
4394 }
4395 }
4396
4397 poparg(*x);
4398 poparg(*y);
4399
4400 char *sorted = cstr;
4401 int sortedlen = totalunique + max(numunique - 1, 0);
4402 if(clen < sortedlen)
4403 {
4404 delete[] cstr;
4405 sorted = newstring(sortedlen);
4406 }
4407
4408 int offset = 0;
4409 loopv(items)
4410 {
4411 sortitem &item = items[i];
4412 if(!item.quotestart) continue;
4413 int len = item.quotelength();
4414 if(i) sorted[offset++] = ' ';
4415 memcpy(&sorted[offset], item.quotestart, len);
4416 offset += len;
4417 }
4418 sorted[offset] = '\0';
4419
4420 commandret->setstr(sorted);
4421 }
4422 COMMAND(0, sortlist, "srree");
4423 ICOMMAND(0, uniquelist, "srre", (char *list, ident *x, ident *y, uint *body), sortlist(list, x, y, NULL, body));
4424
4425 ICOMMAND(0, true, "", (), intret(1));
4426 ICOMMAND(0, false, "", (), intret(0));
4427
4428 #define MATHCMD(name, fmt, type, op, initval, unaryop) \
4429 ICOMMANDS(0, name, #fmt "1V", (tagval *args, int numargs), \
4430 { \
4431 type val; \
4432 if(numargs >= 2) \
4433 { \
4434 val = args[0].fmt; \
4435 type val2 = args[1].fmt; \
4436 op; \
4437 for(int i = 2; i < numargs; i++) { val2 = args[i].fmt; op; } \
4438 } \
4439 else { val = numargs > 0 ? args[0].fmt : initval; unaryop; } \
4440 type##ret(val); \
4441 })
4442 #define MATHICMDN(name, op, initval, unaryop) MATHCMD(#name, i, int, val = val op val2, initval, unaryop)
4443 #define MATHICMD(name, initval, unaryop) MATHICMDN(name, name, initval, unaryop)
4444 #define MATHFCMDN(name, op, initval, unaryop) MATHCMD(#name "f", f, float, val = val op val2, initval, unaryop)
4445 #define MATHFCMD(name, initval, unaryop) MATHFCMDN(name, name, initval, unaryop)
4446
4447 #define CMPCMD(name, fmt, type, op) \
4448 ICOMMANDS(0, name, #fmt "1V", (tagval *args, int numargs), \
4449 { \
4450 bool val; \
4451 if(numargs >= 2) \
4452 { \
4453 val = args[0].fmt op args[1].fmt; \
4454 for(int i = 2; i < numargs && val; i++) val = args[i-1].fmt op args[i].fmt; \
4455 } \
4456 else val = (numargs > 0 ? args[0].fmt : 0) op 0; \
4457 intret(int(val)); \
4458 })
4459 #define CMPICMDN(name, op) CMPCMD(#name, i, int, op)
4460 #define CMPICMD(name) CMPICMDN(name, name)
4461 #define CMPFCMDN(name, op) CMPCMD(#name "f", f, float, op)
4462 #define CMPFCMD(name) CMPFCMDN(name, name)
4463
4464 MATHICMD(+, 0, );
4465 MATHICMD(*, 1, );
4466 MATHICMD(-, 0, val = -val);
4467 MATHICMDN(add, +, 0, );
4468 MATHICMDN(mul, *, 1, );
4469 MATHICMDN(sub, -, 0, val = -val);
4470 CMPICMDN(=, ==);
4471 CMPICMD(!=);
4472 CMPICMD(<);
4473 CMPICMD(>);
4474 CMPICMD(<=);
4475 CMPICMD(>=);
4476 MATHICMD(^, 0, val = ~val);
4477 MATHICMDN(~, ^, 0, val = ~val);
4478 MATHICMD(&, 0, );
4479 MATHICMD(|, 0, );
4480 MATHICMD(^~, 0, );
4481 MATHICMD(&~, 0, );
4482 MATHICMD(|~, 0, );
4483 MATHCMD("<<", i, int, val = val2 < 32 ? val << max(val2, 0) : 0, 0, );
4484 MATHCMD(">>", i, int, val >>= clamp(val2, 0, 31), 0, );
4485
4486 MATHFCMD(+, 0, );
4487 MATHFCMD(*, 1, );
4488 MATHFCMD(-, 0, val = -val);
4489 MATHFCMDN(add, +, 0, );
4490 MATHFCMDN(mul, *, 1, );
4491 MATHFCMDN(sub, -, 0, val = -val);
4492 CMPFCMDN(=, ==);
4493 CMPFCMD(!=);
4494 CMPFCMD(<);
4495 CMPFCMD(>);
4496 CMPFCMD(<=);
4497 CMPFCMD(>=);
4498
4499 ICOMMANDK(0, !, ID_NOT, "t", (tagval *a), intret(getbool(*a) ? 0 : 1));
4500 ICOMMANDK(0, &&, ID_AND, "E1V", (tagval *args, int numargs),
4501 {
4502 if(!numargs) intret(1);
4503 else loopi(numargs)
4504 {
4505 if(i) freearg(*commandret);
4506 if(args[i].type == VAL_CODE) executeret(args[i].code, *commandret);
4507 else *commandret = args[i];
4508 if(!getbool(*commandret)) break;
4509 }
4510 });
4511 ICOMMANDK(0, ||, ID_OR, "E1V", (tagval *args, int numargs),
4512 {
4513 if(!numargs) intret(0);
4514 else loopi(numargs)
4515 {
4516 if(i) freearg(*commandret);
4517 if(args[i].type == VAL_CODE) executeret(args[i].code, *commandret);
4518 else *commandret = args[i];
4519 if(getbool(*commandret)) break;
4520 }
4521 });
4522
4523
4524 #define DIVCMD(name, fmt, type, op) MATHCMD(#name, fmt, type, { if(val2) op; else val = 0; }, 0, )
4525
4526 DIVCMD(div, i, int, val /= val2);
4527 DIVCMD(mod, i, int, val %= val2);
4528 DIVCMD(divf, f, float, val /= val2);
4529 DIVCMD(modf, f, float, val = fmod(val, val2));
4530 MATHCMD("pow", f, float, val = pow(val, val2), 0, );
4531
4532 ICOMMAND(0, sin, "f", (float *a), floatret(sin(*a*RAD)));
4533 ICOMMAND(0, cos, "f", (float *a), floatret(cos(*a*RAD)));
4534 ICOMMAND(0, tan, "f", (float *a), floatret(tan(*a*RAD)));
4535 ICOMMAND(0, asin, "f", (float *a), floatret(asin(*a)/RAD));
4536 ICOMMAND(0, acos, "f", (float *a), floatret(acos(*a)/RAD));
4537 ICOMMAND(0, atan, "f", (float *a), floatret(atan(*a)/RAD));
4538 ICOMMAND(0, atan2, "ff", (float *y, float *x), floatret(atan2(*y, *x)/RAD));
4539 ICOMMAND(0, sqrt, "f", (float *a), floatret(sqrt(*a)));
4540 ICOMMAND(0, loge, "f", (float *a), floatret(log(*a)));
4541 ICOMMAND(0, log2, "f", (float *a), floatret(log(*a)/M_LN2));
4542 ICOMMAND(0, log10, "f", (float *a), floatret(log10(*a)));
4543 ICOMMAND(0, exp, "f", (float *a), floatret(exp(*a)));
4544
4545 #define MINMAXCMD(name, fmt, type, op) \
4546 ICOMMAND(0, name, #fmt "1V", (tagval *args, int numargs), \
4547 { \
4548 type val = numargs > 0 ? args[0].fmt : 0; \
4549 for(int i = 1; i < numargs; i++) val = op(val, args[i].fmt); \
4550 type##ret(val); \
4551 })
4552
4553 MINMAXCMD(min, i, int, min);
4554 MINMAXCMD(max, i, int, max);
4555 MINMAXCMD(minf, f, float, min);
4556 MINMAXCMD(maxf, f, float, max);
4557
4558 #define CLAMPCMD(name, fmt, type, op) \
4559 ICOMMAND(0, name, #fmt "1V", (tagval *args, int numargs), \
4560 { \
4561 type val = numargs > 0 ? args[0].fmt : 0; \
4562 if(numargs >= 2) val = op(val, args[1].fmt, args[2].fmt); \
4563 else if(numargs >= 1) val = op(val, type(0), args[1].fmt); \
4564 type##ret(val); \
4565 })
4566
4567 CLAMPCMD(clamp, i, int, clamp);
4568 CLAMPCMD(clampf, f, float, clamp);
4569
4570 ICOMMAND(0, bitscan, "i", (int *n), intret(bitscan(*n)));
4571
4572 ICOMMAND(0, abs, "i", (int *n), intret(abs(*n)));
4573 ICOMMAND(0, absf, "f", (float *n), floatret(fabs(*n)));
4574 ICOMMAND(0, precf, "fi", (float *a, int *b),
4575 {
4576 defformatstring(format, "%%.%df", max(*b, 0));
4577 defformatstring(retval, format, *a);
4578 result(retval);
4579 });
4580
4581 #define LISTAVGCOMMAND(name, type) \
4582 ICOMMAND(0, name, "V", (tagval *args, int numargs), \
4583 { \
4584 type val = 0; \
4585 loopi(numargs) val += args[i].get##type(); \
4586 if(numargs > 0) val /= type(numargs); \
4587 type##ret(val); \
4588 });
4589
4590 LISTAVGCOMMAND(listavg, int);
4591 LISTAVGCOMMAND(listavgf, float);
4592
4593 ICOMMAND(0, toint, "f", (float *n), intret(int(*n)));
4594 ICOMMAND(0, floor, "f", (float *n), intret(floor(*n)));
4595 ICOMMAND(0, ceil, "f", (float *n), intret(ceil(*n)));
4596 ICOMMAND(0, floorf, "f", (float *n), floatret(floor(*n)));
4597 ICOMMAND(0, ceilf, "f", (float *n), floatret(ceil(*n)));
4598 ICOMMAND(0, round, "ff", (float *n, float *k),
4599 {
4600 double step = *k;
4601 double r = *n;
4602 if(step > 0)
4603 {
4604 r += step * (r < 0 ? -0.5 : 0.5);
4605 r -= fmod(r, step);
4606 }
4607 else r = r < 0 ? ceil(r - 0.5) : floor(r + 0.5);
4608 floatret(float(r));
4609 });
4610
4611 ICOMMAND(0, cond, "ee2V", (tagval *args, int numargs),
4612 {
4613 for(int i = 0; i < numargs; i += 2)
4614 {
4615 if(i+1 < numargs)
4616 {
4617 if(executebool(args[i].code))
4618 {
4619 executeret(args[i+1].code, *commandret);
4620 break;
4621 }
4622 }
4623 else
4624 {
4625 executeret(args[i].code, *commandret);
4626 break;
4627 }
4628 }
4629 });
4630 #define CASECOMMAND(name, fmt, type, acc, compare) \
4631 ICOMMAND(0, name, fmt "te2V", (tagval *args, int numargs), \
4632 { \
4633 type val = acc; \
4634 int i; \
4635 for(i = 1; i+1 < numargs; i += 2) \
4636 { \
4637 if(compare) \
4638 { \
4639 executeret(args[i+1].code, *commandret); \
4640 return; \
4641 } \
4642 } \
4643 })
4644 CASECOMMAND(case, "i", int, args[0].getint(), args[i].type == VAL_NULL || args[i].getint() == val);
4645 CASECOMMAND(casef, "f", float, args[0].getfloat(), args[i].type == VAL_NULL || args[i].getfloat() == val);
4646 CASECOMMAND(cases, "s", const char *, args[0].getstr(), args[i].type == VAL_NULL || !strcmp(args[i].getstr(), val));
4647 CASECOMMAND(cases~, "s", const char *, args[0].getstr(), args[i].type == VAL_NULL || !strcasecmp(args[i].getstr(), val));
4648
4649 ICOMMAND(0, caseif, "te2V", (tagval *args, int numargs),
4650 {
4651 for(int i = 0; i+1 < numargs; i += 2)
4652 {
4653 bool compare = false;
4654 switch(args[i].type)
4655 {
4656 case VAL_NULL: compare = true; break;
4657 case VAL_INT: compare = args[i].getint() != 0; break;
4658 case VAL_FLOAT: compare = args[i].getfloat() != 0; break;
4659 case VAL_CODE: compare = executebool(args[i].code); break;
4660 default: compare = executebool(args[i].getstr()); break;
4661 }
4662 if(compare)
4663 {
4664 executeret(args[i+1].code, *commandret);
4665 return;
4666 }
4667 }
4668 });
4669
4670 ICOMMAND(0, doif, "te2V", (tagval *args, int numargs),
4671 {
4672 for(int i = 0; i+1 < numargs; i += 2)
4673 {
4674 bool compare = false;
4675 switch(args[i].type)
4676 {
4677 case VAL_NULL: compare = true; break;
4678 case VAL_INT: compare = args[i].getint() != 0; break;
4679 case VAL_FLOAT: compare = args[i].getfloat() != 0; break;
4680 case VAL_CODE: compare = executebool(args[i].code); break;
4681 default: compare = executebool(args[i].getstr()); break;
4682 }
4683 if(compare) executeret(args[i+1].code, *commandret);
4684 }
4685 });
4686
4687 ICOMMAND(0, rnd, "ii", (int *a, int *b), intret(*a - *b > 0 ? rnd(*a - *b) + *b : *b));
4688 ICOMMAND(0, rndstr, "i", (int *len),
4689 {
4690 int n = clamp(*len, 0, 10000);
4691 char *s = newstring(n);
4692 for(int i = 0; i < n;)
4693 {
4694 uint r = randomMT();
4695 for(int j = min(i + 4, n); i < j; i++)
4696 {
4697 s[i] = (r%255) + 1;
4698 r /= 255;
4699 }
4700 }
4701 s[n] = '\0';
4702 stringret(s);
4703 });
4704
4705 ICOMMAND(0, tohex, "ii", (int *n, int *p),
4706 {
4707 const int len = 20;
4708 char *buf = newstring(len);
4709 nformatstring(buf, len, "0x%.*X", max(*p, 1), *n);
4710 stringret(buf);
4711 });
4712
4713 ICOMMAND(0, echo, "C", (char *s), conoutf("%s", s));
4714 ICOMMAND(0, error, "C", (char *s), conoutf("\fr%s", s));
4715
4716 #define CMPSCMD(func, name, op) \
4717 ICOMMAND(0, name, "s1V", (tagval *args, int numargs), \
4718 { \
4719 bool val = false; \
4720 if(numargs >= 2) \
4721 { \
4722 val = func(args[0].s, args[1].s) op 0; \
4723 for(int i = 2; i < numargs && val; i++) val = func(args[i-1].s, args[i].s) op 0; \
4724 } \
4725 else val = (numargs > 0 ? args[0].s[0] : 0) op 0; \
4726 intret(val ? 1 : 0); \
4727 })
4728
4729 CMPSCMD(strcmp, strcmp, ==);
4730 CMPSCMD(strcmp, =s, ==);
4731 CMPSCMD(strcmp, !=s, !=);
4732 CMPSCMD(strcmp, <s, <);
4733 CMPSCMD(strcmp, >s, >);
4734 CMPSCMD(strcmp, <=s, <=);
4735 CMPSCMD(strcmp, >=s, >=);
4736
4737 CMPSCMD(strcasecmp, strcasecmp, ==);
4738 CMPSCMD(strcasecmp, ~=s, ==);
4739 CMPSCMD(strcasecmp, !~=s, !=);
4740 CMPSCMD(strcasecmp, <~s, <);
4741 CMPSCMD(strcasecmp, >~s, >);
4742 CMPSCMD(strcasecmp, <~=s, <=);
4743 CMPSCMD(strcasecmp, >~=s, >=);
4744
4745 #define CMPSNCMD(func, name, op) \
4746 ICOMMAND(0, name, "s1V", (tagval *args, int numargs), \
4747 { \
4748 bool val = false; \
4749 if(numargs >= 3) \
4750 { \
4751 int last = numargs-1; \
4752 val = func(args[0].s, args[1].s, args[last].getint()) op 0; \
4753 for(int i = 3; i < last && val; i++) val = func(args[i-1].s, args[i].s, args[last].getint()) op 0; \
4754 } \
4755 else val = (numargs > 0 ? args[0].s[0] : 0) op 0; \
4756 intret(val ? 1 : 0); \
4757 })
4758
4759 CMPSNCMD(strncmp, strncmp, ==);
4760 CMPSNCMD(strncmp, =sn, ==);
4761 CMPSNCMD(strncmp, !=sn, !=);
4762 CMPSNCMD(strncmp, <sn, <);
4763 CMPSNCMD(strncmp, >sn, >);
4764 CMPSNCMD(strncmp, <=sn, <=);
4765 CMPSNCMD(strncmp, >=sn, >=);
4766
4767 CMPSNCMD(strncasecmp, strncasecmp, ==);
4768 CMPSNCMD(strncasecmp, ~=sn, ==);
4769 CMPSNCMD(strncasecmp, !~=sn, !=);
4770 CMPSNCMD(strncasecmp, <~sn, <);
4771 CMPSNCMD(strncasecmp, >~sn, >);
4772 CMPSNCMD(strncasecmp, <~=sn, <=);
4773 CMPSNCMD(strncasecmp, >~=sn, >=);
4774
4775 ICOMMAND(0, strstr, "ss", (char *a, char *b), { char *s = strstr(a, b); intret(s ? s-a : -1); });
4776 ICOMMAND(0, strcasestr, "ss", (char *a, char *b), { char *s = cubecasestr(a, b); intret(s ? s-a : -1); });
4777 ICOMMAND(0, strmatch, "ss", (char *a, char *b), { intret(cubematchstr(a, b) ? 1 : 0); });
4778 ICOMMAND(0, strcasematch, "ss", (char *a, char *b), { intret(cubematchstr(a, b, true) ? 1 : 0); });
4779 ICOMMAND(0, strpattern, "ss", (char *a, char *b), { intret(cubepattern(a, b)); });
4780 ICOMMAND(0, strcasepattern, "ss", (char *a, char *b), { intret(cubepattern(a, b, true)); });
4781
4782 ICOMMAND(0, strlen, "s", (char *s), intret(strlen(s)));
4783 ICOMMAND(0, strcode, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : uchar(s[*i])) : uchar(s[0])));
4784 ICOMMAND(0, codestr, "i", (int *i), { char *s = newstring(1); s[0] = char(*i); s[1] = '\0'; stringret(s); });
4785 ICOMMAND(0, struni, "si", (char *s, int *i), intret(*i > 0 ? (memchr(s, 0, *i) ? 0 : cube2uni(s[*i])) : cube2uni(s[0])));
4786 ICOMMAND(0, unistr, "i", (int *i), { char *s = newstring(1); s[0] = uni2cube(*i); s[1] = '\0'; stringret(s); });
4787
4788 #define STRMAPCOMMAND(name, map) \
4789 ICOMMAND(0, name, "s", (char *s), \
4790 { \
4791 int len = strlen(s); \
4792 char *m = newstring(len); \
4793 loopi(len) m[i] = map(s[i]); \
4794 m[len] = '\0'; \
4795 stringret(m); \
4796 })
4797
4798 STRMAPCOMMAND(strlower, cubelower);
4799 STRMAPCOMMAND(strupper, cubeupper);
4800
strreplace(const char * s,const char * oldval,const char * newval,const char * newval2,bool docase)4801 char *strreplace(const char *s, const char *oldval, const char *newval, const char *newval2, bool docase)
4802 {
4803 vector<char> buf;
4804
4805 int oldlen = strlen(oldval);
4806 if(!oldlen) return newstring(s);
4807 for(int i = 0;; i++)
4808 {
4809 const char *found = docase ? cubecasestr(s, oldval) : strstr(s, oldval);
4810 if(found)
4811 {
4812 while(s < found) buf.add(*s++);
4813 for(const char *n = i&1 ? newval2 : newval; *n; n++) buf.add(*n);
4814 s = found + oldlen;
4815 }
4816 else
4817 {
4818 while(*s) buf.add(*s++);
4819 buf.add('\0');
4820 return newstring(buf.getbuf(), buf.length());
4821 }
4822 }
4823 }
4824
4825 ICOMMAND(0, strreplace, "ssss", (char *s, char *o, char *n, char *n2), commandret->setstr(strreplace(s, o, n, n2[0] ? n2 : n, false)));
4826 ICOMMAND(0, strcasereplace, "ssss", (char *s, char *o, char *n, char *n2), commandret->setstr(strreplace(s, o, n, n2[0] ? n2 : n, true)));
4827
strsplice(const char * s,const char * vals,int * skip,int * count)4828 void strsplice(const char *s, const char *vals, int *skip, int *count)
4829 {
4830 int slen = strlen(s), vlen = strlen(vals),
4831 offset = clamp(*skip, 0, slen),
4832 len = clamp(*count, 0, slen - offset);
4833 char *p = newstring(slen - len + vlen);
4834 if(offset) memcpy(p, s, offset);
4835 if(vlen) memcpy(&p[offset], vals, vlen);
4836 if(offset + len < slen) memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
4837 p[slen - len + vlen] = '\0';
4838 commandret->setstr(p);
4839 }
4840 COMMAND(0, strsplice, "ssii");
4841
4842 struct sleepcmd
4843 {
4844 int delay, millis, flags;
4845 char *command;
4846 };
4847 vector<sleepcmd> sleepcmds;
4848
addsleep(int * msec,char * cmd)4849 void addsleep(int *msec, char *cmd)
4850 {
4851 sleepcmd &s = sleepcmds.add();
4852 s.delay = max(*msec, 1);
4853 s.millis = lastmillis;
4854 s.command = newstring(cmd);
4855 s.flags = identflags;
4856 }
4857
4858 ICOMMAND(0, sleep, "is", (int *a, char *b), addsleep(a, b));
4859
checksleep(int millis)4860 void checksleep(int millis)
4861 {
4862 loopv(sleepcmds)
4863 {
4864 sleepcmd &s = sleepcmds[i];
4865 if(millis - s.millis >= s.delay)
4866 {
4867 char *cmd = s.command; // execute might create more sleep commands
4868 s.command = NULL;
4869 int oldflags = identflags;
4870 identflags = s.flags;
4871 execute(cmd);
4872 identflags = oldflags;
4873 delete[] cmd;
4874 if(sleepcmds.inrange(i) && !sleepcmds[i].command) sleepcmds.remove(i--);
4875 }
4876 }
4877 }
4878
clearsleep(bool clearworlds)4879 void clearsleep(bool clearworlds)
4880 {
4881 int len = 0;
4882 loopv(sleepcmds) if(sleepcmds[i].command)
4883 {
4884 if(!clearworlds || sleepcmds[i].flags&IDF_WORLD)
4885 delete[] sleepcmds[i].command;
4886 else sleepcmds[len++] = sleepcmds[i];
4887 }
4888 sleepcmds.shrink(len);
4889 }
4890
4891 ICOMMAND(0, clearsleep, "i", (int *worlds), clearsleep(*worlds!=0 || identflags&IDF_WORLD));
4892 ICOMMAND(0, exists, "ss", (char *a, char *b), intret(fileexists(a, *b ? b : "r")));
4893 ICOMMAND(0, getmillis, "i", (int *total),
4894 #ifdef STANDALONE
4895 intret(*total > 0 ? totalmillis : (*total < 0 ? (int)enet_time_get() : lastmillis))
4896 #else
4897 intret(*total > 0 ? totalmillis : (*total < 0 ? SDL_GetTicks() : lastmillis))
4898 #endif
4899 );
4900
getvariable(int num)4901 void getvariable(int num)
4902 {
4903 static vector<ident *> ids;
4904 static int lastupdate = 0;
4905 if(ids.empty() || !lastupdate || totalmillis-lastupdate >= 60000)
4906 {
4907 ids.setsize(0);
4908 enumerate(idents, ident, id, ids.add(&id));
4909 lastupdate = totalmillis;
4910 ids.sortname();
4911 }
4912 string text;
4913 num--;
4914 if(ids.inrange(num)) formatstring(text, "%s", ids[num]->name);
4915 else formatstring(text, "%d", ids.length());
4916 result(text);
4917 }
4918 ICOMMAND(0, getvariable, "i", (int *n), getvariable(*n));
4919
getvarinfo(int n,int types,int notypes,int flags,int noflags,char * str)4920 void getvarinfo(int n, int types, int notypes, int flags, int noflags, char *str)
4921 {
4922 static vector<ident *> ids[2];
4923 static int lastupdate = 0, lasttypes = 0, lastnotypes = 0, lastflags = 0, lastnoflags = 0, curids = 0;
4924 if(ids[0].empty() || !lastupdate || types != lasttypes || notypes != lastnotypes || flags != lastflags || noflags != lastnoflags || totalmillis-lastupdate >= 60000)
4925 {
4926 loopi(2) ids[i].setsize(0);
4927 enumerate(idents, ident, id, if((!types || (1<<id.type)&types) && (!notypes || !((1<<id.type)¬ypes)) && (!flags || id.flags&flags) && (!noflags || !(id.flags&noflags))) ids[0].add(&id));
4928 lastupdate = totalmillis;
4929 lasttypes = types;
4930 lastnotypes = notypes;
4931 lastflags = flags;
4932 lastnoflags = noflags;
4933 ids[0].sortname();
4934 }
4935 if(str && *str)
4936 {
4937 static char *laststr = NULL;
4938 if(ids[1].empty() || !laststr || strcmp(str, laststr))
4939 {
4940 ids[1].setsize(0);
4941 loopv(ids[0]) if(cubecasestr(ids[0][i]->name, str)) ids[1].add(ids[0][i]);
4942 if(laststr) DELETEA(laststr);
4943 laststr = newstring(str);
4944 }
4945 curids = 1;
4946 }
4947 else curids = 0;
4948 if(n < 0) intret(ids[curids].length());
4949 else if(ids[curids].inrange(n)) result(ids[curids][n]->name);
4950 }
4951
4952 ICOMMAND(0, getvarinfo, "biiiis", (int *n, int *w, int *x, int *t, int *o, char *s), getvarinfo(*n, *w, *x, *t, *o, s));
4953
hexcolour(int * n)4954 void hexcolour(int *n)
4955 {
4956 defformatstring(s, *n >= 0 && uint(*n) <= 0xFFFFFFFFU ? (*n <= 0xFFFFFF ? "0x%.6X" : "0x%.8X") : "%d", *n);
4957 result(s);
4958 }
4959 COMMAND(0, hexcolour, "i");
4960
genkey(char * s)4961 void genkey(char *s)
4962 {
4963 vector<char> privkey, pubkey;
4964 genprivkey(s, privkey, pubkey);
4965 defformatstring(keybuf, "%s %s", privkey.getbuf(), pubkey.getbuf());
4966 result(keybuf);
4967 }
4968 COMMAND(0, genkey, "s");
4969
hasflag(const char * flags,char f)4970 bool hasflag(const char *flags, char f)
4971 {
4972 for(const char *c = flags; *c; c++)
4973 if(*c == f) return true;
4974 return false;
4975 }
4976 ICOMMAND(0, hasflag, "ss", (char *s, char *f), intret(*s && *f && hasflag(s, *f) ? 1 : 0));
4977
modcolour(int c,float m)4978 int modcolour(int c, float m) { return vec::fromcolor(c).mul(m).tohexcolor(); }
4979 ICOMMAND(0, modcolour, "if", (int *c, float *m), intret(modcolour(*c, *m)));
4980
mulcolour(int c,int m)4981 int mulcolour(int c, int m) { return vec::fromcolor(c).mul(vec::fromcolor(m)).tohexcolor(); }
4982 ICOMMAND(0, mulcolour, "ii", (int *c, int *m), intret(mulcolour(*c, *m)));
4983
skewcolour(int c,int m,float s)4984 int skewcolour(int c, int m, float s) { return vec::fromcolor(c).mul(1-s).add(vec::fromcolor(m).mul(s)).clamp(0.f, 1.f).tohexcolor(); }
4985 ICOMMAND(0, skewcolour, "iif", (int *c, int *m, float *s), intret(skewcolour(*c, *m, clamp(*s, 0.f, 1.f))));
4986
limitstring(const char * str,size_t len)4987 char *limitstring(const char *str, size_t len)
4988 {
4989 static bigstring limitstrtext;
4990 copystring(limitstrtext, str, len);
4991 return limitstrtext;
4992 }
4993 ICOMMAND(0, limitstring, "si", (char *s, int *n), result(limitstring(s, *n)));
4994