Line data Source code
1 : /* cubescript commands
2 : *
3 : * these functions & assignment macros define standard functions used with the language
4 : * the language does not otherwise define special operators (besides bracket semantics)
5 : *
6 : * including, but not limited to:
7 : * - file handling
8 : * - arithmetic and boolean operators
9 : * - control statements
10 : */
11 :
12 : #include "../libprimis-headers/cube.h"
13 : #include "../../shared/stream.h"
14 :
15 : #include "console.h"
16 : #include "control.h"
17 : #include "cs.h"
18 :
19 : #include "render/hud.h"
20 :
21 : /* execfile
22 : *
23 : * executes the cubescript in a file located at a relative path to the game's home dir
24 : *
25 : * *cfgfile: a pointer to the name of the file
26 : * msg: if true, prints out a message if there is no file found
27 : */
28 7 : bool execfile(const char *cfgfile, bool msg)
29 : {
30 : string s;
31 7 : copystring(s, cfgfile);
32 7 : char *buf = loadfile(path(s), nullptr);
33 7 : if(!buf)
34 : {
35 7 : if(msg)
36 : {
37 1 : conoutf(Console_Error, "could not read \"%s\"", cfgfile);
38 : }
39 7 : return false;
40 : }
41 0 : const char *oldsourcefile = sourcefile,
42 0 : *oldsourcestr = sourcestr;
43 0 : sourcefile = cfgfile;
44 0 : sourcestr = buf;
45 0 : execute(buf);
46 0 : sourcefile = oldsourcefile;
47 0 : sourcestr = oldsourcestr;
48 0 : delete[] buf;
49 0 : return true;
50 : }
51 :
52 : //cmd
53 1 : static void exec(const char *file, int *msg)
54 : {
55 1 : intret(execfile(file, *msg != 0) ? 1 : 0);
56 1 : }
57 :
58 : //excapes strings by converting \<special char> to ^<special char>
59 : // ^ is the escape char in cubescript
60 3 : const char *escapestring(const char *s)
61 : {
62 3 : stridx = (stridx + 1)%4;
63 3 : std::vector<char> &buf = strbuf[stridx];
64 3 : buf.clear();
65 3 : buf.push_back('"');
66 55 : for(; *s; s++)
67 : {
68 52 : switch(*s)
69 : {
70 2 : case '\n':
71 : {
72 2 : buf.push_back('^');
73 2 : buf.push_back('n');
74 2 : break;
75 : }
76 2 : case '\t':
77 : {
78 2 : buf.push_back('^');
79 2 : buf.push_back('t');
80 2 : break;
81 : }
82 2 : case '\f':
83 : {
84 2 : buf.push_back('^');
85 2 : buf.push_back('f');
86 2 : break;
87 : }
88 1 : case '"':
89 : {
90 1 : buf.push_back('^');
91 1 : buf.push_back('"');
92 1 : break;
93 : }
94 1 : case '^':
95 : {
96 1 : buf.push_back('^');
97 1 : buf.push_back('^');
98 1 : break;
99 : }
100 44 : default:
101 : {
102 44 : buf.push_back(*s);
103 44 : break;
104 : }
105 : }
106 : }
107 3 : buf.push_back('\"');
108 3 : buf.push_back('\0');
109 3 : return buf.data();
110 : }
111 :
112 1 : static void escapecmd(char *s)
113 : {
114 1 : result(escapestring(s));
115 1 : }
116 :
117 1 : static void unescapecmd(char *s)
118 : {
119 1 : int len = std::strlen(s);
120 1 : char *d = newstring(len);
121 1 : unescapestring(d, s, &s[len]);
122 1 : stringret(d);
123 1 : }
124 :
125 2 : const char *escapeid(const char *s)
126 : {
127 2 : const char *end = s + std::strcspn(s, "\"/;()[]@ \f\t\r\n\0");
128 2 : return *end ? escapestring(s) : s;
129 : }
130 :
131 16 : bool validateblock(const char *s)
132 : {
133 16 : constexpr int maxbrak = 100;
134 : static std::array<char, maxbrak> brakstack;
135 16 : int brakdepth = 0;
136 308 : for(; *s; s++)
137 : {
138 302 : switch(*s)
139 : {
140 220 : case '[':
141 : case '(':
142 : {
143 220 : if(brakdepth >= maxbrak)
144 : {
145 2 : return false;
146 : }
147 218 : brakstack[brakdepth++] = *s;
148 218 : break;
149 : }
150 8 : case ']':
151 : {
152 8 : if(brakdepth <= 0 || brakstack[--brakdepth] != '[')
153 : {
154 2 : return false;
155 : }
156 6 : break;
157 : }
158 8 : case ')':
159 : {
160 8 : if(brakdepth <= 0 || brakstack[--brakdepth] != '(')
161 : {
162 2 : return false;
163 : }
164 6 : break;
165 : }
166 2 : case '"':
167 : {
168 2 : s = parsestring(s + 1);
169 2 : if(*s != '"')
170 : {
171 1 : return false;
172 : }
173 1 : break;
174 : }
175 2 : case '/':
176 : {
177 2 : if(s[1] == '/')
178 : {
179 1 : return false;
180 : }
181 1 : break;
182 : }
183 2 : case '@':
184 : case '\f':
185 : {
186 2 : return false;
187 : }
188 : }
189 : }
190 6 : return brakdepth == 0;
191 : }
192 :
193 0 : static const char *escapeid(ident &id)
194 : {
195 0 : return escapeid(id.name);
196 : }
197 :
198 1 : void writecfg(const char *savedconfig, const char *autoexec, const char *defaultconfig, const char *name)
199 : {
200 1 : std::fstream f;
201 1 : conoutf("writing to %s", copypath(name && name[0] ? name : savedconfig));
202 1 : f.open(copypath(name && name[0] ? name : savedconfig), std::ios::out);
203 1 : if(!f.is_open())
204 : {
205 1 : conoutf("file not opened, config not written");
206 1 : return;
207 : }
208 : //write the top of file comment manually
209 0 : f << "// automatically written on exit, DO NOT MODIFY\n// delete this file to have";
210 0 : if(defaultconfig)
211 : {
212 0 : f << defaultconfig;
213 : }
214 0 : f << "overwrite these settings\n// modify settings in game, or put settings in";
215 0 : if(autoexec)
216 : {
217 0 : f << autoexec;
218 : }
219 0 : f << "to override anything\n\n";
220 0 : writecrosshairs(f);
221 0 : std::vector<ident *> ids;
222 0 : for(auto& [k, id] : idents)
223 : {
224 0 : ids.push_back(&id);
225 : }
226 0 : std::sort(ids.begin(), ids.end());
227 0 : for(uint i = 0; i < ids.size(); i++)
228 : {
229 0 : ident &id = *ids[i];
230 0 : if(id.flags&Idf_Persist)
231 : {
232 0 : switch(id.type)
233 : {
234 0 : case Id_Var:
235 : {
236 0 : f << escapeid(id) << " " << *id.storage.i << std::endl;
237 0 : break;
238 : }
239 0 : case Id_FloatVar:
240 : {
241 0 : f << escapeid(id) << " " << floatstr(*id.storage.f) << std::endl;
242 0 : break;
243 : }
244 0 : case Id_StringVar:
245 : {
246 0 : f << escapeid(id) << " " << escapestring(*id.storage.s) << std::endl;
247 0 : break;
248 : }
249 : }
250 : }
251 : }
252 0 : writebinds(f);
253 0 : for(ident *&id : ids)
254 : {
255 0 : if(id->type==Id_Alias && id->flags&Idf_Persist && !(id->flags&Idf_Overridden))
256 : {
257 0 : switch(id->valtype)
258 : {
259 0 : case Value_String:
260 : {
261 0 : if(!id->val.s[0])
262 : {
263 0 : break;
264 : }
265 0 : if(!validateblock(id->val.s))
266 : {
267 0 : f << escapeid(*id) << " = " << escapestring(id->val.s) << std::endl;
268 0 : break;
269 : }
270 : }
271 : [[fallthrough]];
272 : case Value_Float:
273 : case Value_Integer:
274 : {
275 0 : f << escapeid(*id) << " = [" << id->getstr() << "]" << std::endl;
276 0 : break;
277 : }
278 : }
279 : }
280 : }
281 0 : writecompletions(f);
282 0 : f.close();
283 1 : }
284 :
285 1 : void changedvars()
286 : {
287 1 : std::vector<const ident *> ids;
288 1107 : for(const auto& [k, id] : idents)
289 : {
290 1106 : if(id.flags&Idf_Overridden)
291 : {
292 0 : ids.push_back(&id);
293 : }
294 : }
295 1 : std::sort(ids.begin(), ids.end());
296 1 : for(const ident *i: ids)
297 : {
298 0 : printvar(i);
299 : }
300 1 : }
301 :
302 : static string retbuf[4];
303 : static int retidx = 0;
304 :
305 60 : const char *intstr(int v)
306 : {
307 60 : retidx = (retidx + 1)%4;
308 60 : intformat(retbuf[retidx], v);
309 60 : return retbuf[retidx];
310 : }
311 :
312 531 : void intret(int v)
313 : {
314 531 : commandret->setint(v);
315 531 : }
316 :
317 112 : const char *floatstr(float v)
318 : {
319 112 : retidx = (retidx + 1)%4;
320 112 : floatformat(retbuf[retidx], v);
321 112 : return retbuf[retidx];
322 : }
323 :
324 161 : void floatret(float v)
325 : {
326 161 : commandret->setfloat(v);
327 161 : }
328 :
329 0 : const char *numberstr(double v)
330 : {
331 0 : auto numberformat = [] (char *buf, double v, int len = 20)
332 : {
333 0 : int i = static_cast<int>(v);
334 0 : if(v == i)
335 : {
336 0 : nformatstring(buf, len, "%d", i);
337 : }
338 : else
339 : {
340 0 : nformatstring(buf, len, "%.7g", v);
341 : }
342 0 : };
343 :
344 0 : retidx = (retidx + 1)%4;
345 0 : numberformat(retbuf[retidx], v);
346 0 : return retbuf[retidx];
347 : }
348 :
349 0 : void loopiter(ident &id, identstack &stack, const tagval &v)
350 : {
351 0 : if(id.stack != &stack)
352 : {
353 0 : pusharg(id, v, stack);
354 0 : id.flags &= ~Idf_Unknown;
355 : }
356 : else
357 : {
358 0 : if(id.valtype == Value_String)
359 : {
360 0 : delete[] id.val.s;
361 : }
362 0 : cleancode(id);
363 0 : id.setval(v);
364 : }
365 0 : }
366 :
367 0 : void loopiter(ident *id, identstack &stack, int i)
368 : {
369 : tagval v;
370 0 : v.setint(i);
371 0 : loopiter(*id, stack, v);
372 0 : }
373 :
374 0 : void loopend(ident *id, identstack &stack)
375 : {
376 0 : if(id->stack == &stack)
377 : {
378 0 : poparg(*id);
379 : }
380 0 : }
381 :
382 221 : static void setiter(ident &id, int i, identstack &stack)
383 : {
384 221 : if(id.stack == &stack)
385 : {
386 171 : if(id.valtype != Value_Integer)
387 : {
388 0 : if(id.valtype == Value_String)
389 : {
390 0 : delete[] id.val.s;
391 : }
392 0 : cleancode(id);
393 0 : id.valtype = Value_Integer;
394 : }
395 171 : id.val.i = i;
396 : }
397 : else
398 : {
399 : tagval t;
400 50 : t.setint(i);
401 50 : pusharg(id, t, stack);
402 50 : id.flags &= ~Idf_Unknown;
403 : }
404 221 : }
405 :
406 30 : static void doloop(ident &id, int offset, int n, int step, uint *body)
407 : {
408 30 : if(n <= 0 || id.type != Id_Alias)
409 : {
410 12 : return;
411 : }
412 : identstack stack;
413 162 : for(int i = 0; i < n; ++i)
414 : {
415 144 : setiter(id, offset + i*step, stack);
416 144 : execute(body);
417 : }
418 18 : poparg(id);
419 : }
420 :
421 39 : static void loopconc(ident &id, int offset, int n, uint *body, bool space)
422 : {
423 39 : if(n <= 0 || id.type != Id_Alias)
424 : {
425 7 : return;
426 : }
427 : identstack stack;
428 32 : std::vector<char> s;
429 109 : for(int i = 0; i < n; ++i)
430 : {
431 77 : setiter(id, offset + i, stack);
432 : tagval v;
433 77 : executeret(body, v);
434 77 : const char *vstr = v.getstr();
435 77 : int len = std::strlen(vstr);
436 77 : if(space && i)
437 : {
438 45 : s.push_back(' ');
439 : }
440 216 : for(int j = 0; j < len; ++j)
441 : {
442 139 : s.push_back(vstr[j]);
443 : }
444 77 : freearg(v);
445 : }
446 32 : if(n > 0)
447 : {
448 32 : poparg(id);
449 : }
450 32 : s.push_back('\0');
451 32 : char * arr = new char[s.size()];
452 32 : std::memcpy(arr, s.data(), s.size());
453 32 : commandret->setstr(arr);
454 32 : }
455 :
456 13 : void concatword(tagval *v, int n)
457 : {
458 13 : commandret->setstr(conc(v, n, false));
459 13 : }
460 :
461 2 : void append(ident *id, tagval *v, bool space)
462 : {
463 2 : if(id->type != Id_Alias || v->type == Value_Null)
464 : {
465 2 : return;
466 : }
467 : tagval r;
468 0 : const char *prefix = id->getstr();
469 0 : if(prefix[0])
470 : {
471 0 : r.setstr(conc(v, 1, space, prefix));
472 : }
473 : else
474 : {
475 0 : v->getval(r);
476 : }
477 0 : if(id->index < Max_Args)
478 : {
479 0 : setarg(*id, r);
480 : }
481 : else
482 : {
483 0 : setalias(*id, r);
484 : }
485 :
486 : }
487 :
488 6 : void result(tagval &v)
489 : {
490 6 : *commandret = v;
491 6 : v.type = Value_Null;
492 6 : }
493 :
494 31 : void stringret(char *s)
495 : {
496 31 : commandret->setstr(s);
497 31 : }
498 :
499 15 : void result(const char *s)
500 : {
501 15 : commandret->setstr(newstring(s));
502 15 : }
503 :
504 7 : void format(tagval *args, int numargs)
505 : {
506 7 : std::vector<char> s;
507 7 : if(!args)
508 : {
509 0 : conoutf(Console_Error, "no parameters to format");
510 0 : return;
511 : }
512 7 : const char *f = args[0].getstr();
513 32 : while(*f)
514 : {
515 25 : int c = *f++;
516 25 : if(c == '%')
517 : {
518 0 : int i = *f++;
519 0 : if(i >= '1' && i <= '9')
520 : {
521 0 : i -= '0';
522 0 : const char *sub = i < numargs ? args[i].getstr() : "";
523 0 : while(*sub)
524 : {
525 0 : s.push_back(*sub++);
526 : }
527 0 : }
528 : else
529 : {
530 0 : s.push_back(i);
531 : }
532 : }
533 : else
534 : {
535 25 : s.push_back(c);
536 : }
537 : }
538 7 : s.push_back('\0');
539 : //create new array to pass back
540 7 : char * arr = new char[s.size()];
541 7 : std::memcpy(arr, s.data(), s.size());
542 7 : commandret->setstr(arr);
543 7 : }
544 :
545 : static const char *liststart = nullptr,
546 : *listend = nullptr,
547 : *listquotestart = nullptr,
548 : *listquoteend = nullptr;
549 :
550 788 : static void skiplist(const char *&p)
551 : {
552 : for(;;)
553 : {
554 788 : p += std::strspn(p, " \t\r\n");
555 788 : if(p[0]!='/' || p[1]!='/')
556 : {
557 : break;
558 : }
559 4 : p += std::strcspn(p, "\n\0");
560 : }
561 784 : }
562 :
563 449 : static bool parselist(const char *&s, const char *&start = liststart, const char *&end = listend, const char *"estart = listquotestart, const char *"eend = listquoteend)
564 : {
565 449 : skiplist(s);
566 449 : switch(*s)
567 : {
568 2 : case '"':
569 : {
570 2 : quotestart = s++;
571 2 : start = s;
572 2 : s = parsestring(s);
573 2 : end = s;
574 2 : if(*s == '"')
575 : {
576 2 : s++;
577 : }
578 2 : quoteend = s;
579 2 : break;
580 : }
581 7 : case '(':
582 : case '[':
583 7 : quotestart = s;
584 7 : start = s+1;
585 7 : for(int braktype = *s++, brak = 1;;)
586 : {
587 7 : s += std::strcspn(s, "\"/;()[]\0");
588 7 : int c = *s++;
589 : switch(c)
590 : {
591 1 : case '\0':
592 : {
593 1 : s--;
594 1 : quoteend = end = s;
595 1 : return true;
596 : }
597 0 : case '"':
598 : {
599 0 : s = parsestring(s);
600 0 : if(*s == '"')
601 : {
602 0 : s++;
603 : }
604 0 : break;
605 : }
606 0 : case '/':
607 : {
608 0 : if(*s == '/')
609 : {
610 0 : s += std::strcspn(s, "\n\0");
611 : }
612 0 : break;
613 : }
614 0 : case '(':
615 : case '[':
616 : {
617 0 : if(c == braktype)
618 : {
619 0 : brak++;
620 : }
621 0 : break;
622 : }
623 3 : case ')':
624 : {
625 3 : if(braktype == '(' && --brak <= 0)
626 : {
627 3 : goto endblock;
628 : }
629 0 : break;
630 : }
631 3 : case ']':
632 : {
633 3 : if(braktype == '[' && --brak <= 0)
634 : {
635 3 : goto endblock;
636 : }
637 0 : break;
638 : }
639 : }
640 0 : }
641 6 : endblock:
642 6 : end = s-1;
643 6 : quoteend = s;
644 6 : break;
645 122 : case '\0':
646 : case ')':
647 : case ']':
648 : {
649 122 : return false;
650 : }
651 318 : default:
652 : {
653 318 : quotestart = start = s;
654 318 : s = parseword(s);
655 318 : quoteend = end = s;
656 318 : break;
657 : }
658 : }
659 326 : skiplist(s);
660 326 : if(*s == ';')
661 : {
662 0 : s++;
663 : }
664 326 : return true;
665 : }
666 :
667 40 : static char *listelem(const char *start = liststart, const char *end = listend, const char *quotestart = listquotestart)
668 : {
669 40 : size_t len = end-start;
670 40 : char *s = newstring(len);
671 40 : if(*quotestart == '"')
672 : {
673 2 : unescapestring(s, start, end);
674 : }
675 : else
676 : {
677 38 : std::memcpy(s, start, len);
678 38 : s[len] = '\0';
679 : }
680 40 : return s;
681 : }
682 :
683 8 : void explodelist(const char *s, std::vector<char *> &elems, int limit)
684 : {
685 : const char *start, *end, *qstart;
686 21 : while((limit < 0 || static_cast<int>(elems.size()) < limit) && parselist(s, start, end, qstart))
687 : {
688 13 : elems.push_back(listelem(start, end, qstart));
689 : }
690 8 : }
691 :
692 9 : void explodelist(const char *s, std::vector<std::string> &elems, int limit)
693 : {
694 : const char *start, *end, *qstart;
695 23 : while((limit < 0 || static_cast<int>(elems.size()) < limit) && parselist(s, start, end, qstart))
696 : {
697 14 : char *s = listelem(start, end, qstart);
698 14 : elems.push_back(std::string(s));
699 14 : delete[] s;
700 : }
701 9 : }
702 :
703 15 : static int listlen(const char *s)
704 : {
705 15 : int n = 0;
706 47 : while(parselist(s))
707 : {
708 32 : n++;
709 : }
710 15 : return n;
711 : }
712 :
713 4 : static void listlencmd(const char *s)
714 : {
715 4 : intret(listlen(s));
716 4 : }
717 :
718 7 : static void at(tagval *args, int numargs)
719 : {
720 7 : if(!numargs)
721 : {
722 0 : return;
723 : }
724 7 : const char *start = args[0].getstr(),
725 7 : *end = start + std::strlen(start),
726 7 : *qstart = "";
727 14 : for(int i = 1; i < numargs; i++)
728 : {
729 7 : const char *list = start;
730 7 : int pos = args[i].getint();
731 11 : for(; pos > 0; pos--)
732 : {
733 4 : if(!parselist(list))
734 : {
735 0 : break;
736 : }
737 : }
738 7 : if(pos > 0 || !parselist(list, start, end, qstart))
739 : {
740 2 : start = end = qstart = "";
741 : }
742 : }
743 7 : commandret->setstr(listelem(start, end, qstart));
744 : }
745 :
746 11 : static void sublist(const char *s, const int *skip, const int *count, const int *numargs)
747 : {
748 11 : int offset = std::max(*skip, 0),
749 11 : len = *numargs >= 3 ? std::max(*count, 0) : -1;
750 16 : for(int i = 0; i < offset; ++i)
751 : {
752 5 : if(!parselist(s))
753 : {
754 0 : break;
755 : }
756 : }
757 11 : if(len < 0)
758 : {
759 4 : if(offset > 0)
760 : {
761 1 : skiplist(s);
762 : }
763 4 : commandret->setstr(newstring(s));
764 4 : return;
765 : }
766 7 : const char *list = s,
767 : *start, *end, *qstart,
768 7 : *qend = s;
769 7 : if(len > 0 && parselist(s, start, end, list, qend))
770 : {
771 10 : while(--len > 0 && parselist(s, start, end, qstart, qend))
772 : {
773 : //(empty body)
774 : }
775 : }
776 7 : commandret->setstr(newstring(list, qend - list));
777 : }
778 :
779 97 : static void setiter(ident &id, char *val, identstack &stack)
780 : {
781 97 : if(id.stack == &stack)
782 : {
783 81 : if(id.valtype == Value_String)
784 : {
785 81 : delete[] id.val.s;
786 : }
787 : else
788 : {
789 0 : id.valtype = Value_String;
790 : }
791 81 : cleancode(id);
792 81 : id.val.s = val;
793 : }
794 : else
795 : {
796 : tagval t;
797 16 : t.setstr(val);
798 16 : pusharg(id, t, stack);
799 16 : id.flags &= ~Idf_Unknown;
800 : }
801 97 : }
802 :
803 15 : void listfind(ident *id, const char *list, const uint *body)
804 : {
805 15 : if(id->type!=Id_Alias)
806 : {
807 2 : intret(-1);
808 2 : return;
809 : }
810 : identstack stack;
811 13 : int n = -1;
812 22 : for(const char *s = list, *start, *end; parselist(s, start, end);)
813 : {
814 16 : ++n;
815 16 : setiter(*id, newstring(start, end-start), stack);
816 16 : if(executebool(body))
817 : {
818 7 : intret(n);
819 7 : goto found;
820 : }
821 : }
822 6 : intret(-1); //if element not found in list
823 13 : found: //if element is found in list
824 13 : if(n >= 0)
825 : {
826 10 : poparg(*id);
827 : }
828 : }
829 :
830 : //note: the goto here is the opposite of listfind above: goto triggers when elem not found
831 1 : void listfindeq(char *list, int *val, int *skip)
832 : {
833 1 : int n = 0;
834 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
835 : {
836 0 : if(parseint(start) == *val)
837 : {
838 0 : intret(n);
839 0 : return;
840 : }
841 0 : for(int i = 0; i < static_cast<int>(*skip); ++i)
842 : {
843 0 : if(!parselist(s))
844 : {
845 0 : goto notfound;
846 : }
847 0 : n++;
848 : }
849 : }
850 1 : notfound:
851 1 : intret(-1);
852 : }
853 :
854 11 : void listassoceq(char *list, int *val)
855 : {
856 14 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end);)
857 : {
858 12 : if(parseint(start) == *val)
859 : {
860 7 : if(parselist(s, start, end, qstart))
861 : {
862 6 : stringret(listelem(start, end, qstart));
863 : }
864 7 : return;
865 : }
866 5 : if(!parselist(s))
867 : {
868 2 : break;
869 : }
870 : }
871 : }
872 :
873 2 : void looplistconc(ident *id, const char *list, const uint *body, bool space)
874 : {
875 2 : if(id->type!=Id_Alias)
876 : {
877 0 : return;
878 : }
879 : identstack stack;
880 2 : std::vector<char> r;
881 2 : int n = 0;
882 2 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
883 : {
884 0 : char *val = listelem(start, end, qstart);
885 0 : setiter(*id, val, stack);
886 0 : if(n && space)
887 : {
888 0 : r.push_back(' ');
889 : }
890 : tagval v;
891 0 : executeret(body, v);
892 0 : const char *vstr = v.getstr();
893 0 : int len = std::strlen(vstr);
894 0 : for(int i = 0; i < len; ++i)
895 : {
896 0 : r.push_back(vstr[i]);
897 : }
898 0 : freearg(v);
899 : }
900 2 : if(n)
901 : {
902 0 : poparg(*id);
903 : }
904 2 : r.push_back('\0');
905 2 : char * arr = new char[r.size()];
906 2 : std::memcpy(arr, r.data(), r.size());
907 2 : commandret->setstr(arr);
908 2 : }
909 :
910 9 : void listcount(ident *id, const char *list, const uint *body)
911 : {
912 9 : if(id->type!=Id_Alias)
913 : {
914 0 : return;
915 : }
916 : identstack stack;
917 9 : int n = 0,
918 9 : r = 0;
919 21 : for(const char *s = list, *start, *end; parselist(s, start, end); n++)
920 : {
921 12 : char *val = newstring(start, end-start);
922 12 : setiter(*id, val, stack);
923 12 : if(executebool(body))
924 : {
925 10 : r++;
926 : }
927 : }
928 9 : if(n)
929 : {
930 5 : poparg(*id);
931 : }
932 9 : intret(r);
933 : }
934 :
935 11 : void prettylist(const char *s, const char *conj)
936 : {
937 11 : std::vector<char> p;
938 : const char *start, *end, *qstart;
939 39 : for(int len = listlen(s), n = 0; parselist(s, start, end, qstart); n++)
940 : {
941 28 : if(*qstart == '"')
942 : {
943 0 : p.reserve(end - start + 1);
944 :
945 0 : for(int i = 0; i < end - start + 1; ++i)
946 : {
947 0 : p.emplace_back();
948 : }
949 0 : unescapestring(&(*(p.end() - (end - start + 1))), start, end);
950 : }
951 : else
952 : {
953 91 : for(int i = 0; i < end - start; ++i)
954 : {
955 63 : p.push_back(start[i]);
956 : }
957 : }
958 28 : if(n+1 < len)
959 : {
960 20 : if(len > 2 || !conj[0])
961 : {
962 19 : p.push_back(',');
963 : }
964 20 : if(n+2 == len && conj[0])
965 : {
966 5 : p.push_back(' ');
967 17 : for(size_t i = 0; i < std::strlen(conj); ++i)
968 : {
969 12 : p.push_back(conj[i]);
970 : }
971 : }
972 20 : p.push_back(' ');
973 : }
974 : }
975 11 : p.push_back('\0');
976 : //create new array to pass back
977 11 : char * arr = new char[p.size()];
978 11 : std::memcpy(arr, p.data(), p.size());
979 11 : commandret->setstr(arr);
980 11 : }
981 :
982 : //returns the int position of the needle inside the passed list
983 47 : int listincludes(const char *list, const char *needle, int needlelen)
984 : {
985 47 : int offset = 0;
986 91 : for(const char *s = list, *start, *end; parselist(s, start, end);)
987 : {
988 77 : int len = end - start;
989 77 : if(needlelen == len && !std::strncmp(needle, start, len))
990 : {
991 33 : return offset;
992 : }
993 44 : offset++;
994 : }
995 14 : return -1;
996 : }
997 :
998 8 : void listsplice(const char *s, const char *vals, int *skip, int *count)
999 : {
1000 8 : int offset = std::max(*skip, 0),
1001 8 : len = std::max(*count, 0);
1002 8 : const char *list = s,
1003 : *start, *end, *qstart,
1004 8 : *qend = s;
1005 14 : for(int i = 0; i < offset; ++i)
1006 : {
1007 6 : if(!parselist(s, start, end, qstart, qend))
1008 : {
1009 0 : break;
1010 : }
1011 : }
1012 8 : std::vector<char> p;
1013 8 : if(qend > list)
1014 : {
1015 36 : for(int i = 0; i < qend - list; ++i)
1016 : {
1017 32 : p.push_back(list[i]);
1018 : }
1019 : }
1020 8 : if(*vals)
1021 : {
1022 6 : if(!p.empty())
1023 : {
1024 4 : p.push_back(' ');
1025 : }
1026 51 : for(size_t i = 0; i < std::strlen(vals); ++i)
1027 : {
1028 45 : p.push_back(vals[i]);
1029 : }
1030 : }
1031 12 : for(int i = 0; i < len; ++i)
1032 : {
1033 5 : if(!parselist(s))
1034 : {
1035 1 : break;
1036 : }
1037 : }
1038 8 : skiplist(s);
1039 8 : switch(*s)
1040 : {
1041 6 : case '\0':
1042 : case ')':
1043 : case ']':
1044 : {
1045 6 : break;
1046 : }
1047 2 : default:
1048 : {
1049 2 : if(!p.empty())
1050 : {
1051 2 : p.push_back(' ');
1052 : }
1053 14 : for(size_t i = 0; i < std::strlen(s); ++i)
1054 : {
1055 12 : p.push_back(s[i]);
1056 : }
1057 2 : break;
1058 : }
1059 : }
1060 8 : p.push_back('\0');
1061 8 : char * arr = new char[p.size()];
1062 8 : std::memcpy(arr, p.data(), p.size());
1063 8 : commandret->setstr(arr);
1064 8 : }
1065 :
1066 : //executes the body for each file in the given path, using ident passed
1067 1 : void loopfiles(ident *id, char *dir, char *ext, uint *body)
1068 : {
1069 1 : if(id->type!=Id_Alias)
1070 : {
1071 0 : return;
1072 : }
1073 : identstack stack;
1074 1 : std::vector<char *> files;
1075 1 : listfiles(dir, ext[0] ? ext : nullptr, files);
1076 1 : std::sort(files.begin(), files.end());
1077 70 : for(uint i = 0; i < files.size(); i++)
1078 : {
1079 69 : setiter(*id, files[i], stack);
1080 69 : execute(body);
1081 : }
1082 1 : if(files.size())
1083 : {
1084 1 : poparg(*id);
1085 : }
1086 1 : }
1087 :
1088 1 : static void findfile_(char *name)
1089 : {
1090 : string fname;
1091 1 : copystring(fname, name);
1092 1 : path(fname);
1093 1 : intret(
1094 1 : findzipfile(fname) ||
1095 1 : fileexists(fname, "e") || findfile(fname, "e") ? 1 : 0
1096 : );
1097 1 : }
1098 :
1099 18 : void sortlist(char *list, ident *x, ident *y, uint *body, uint *unique)
1100 : {
1101 : struct SortItem
1102 : {
1103 : const char *str, *quotestart, *quoteend;
1104 :
1105 104 : size_t quotelength() const
1106 : {
1107 104 : return static_cast<size_t>(quoteend-quotestart);
1108 : }
1109 : };
1110 :
1111 : struct SortFunction
1112 : {
1113 : ident *x, *y;
1114 : uint *body;
1115 :
1116 47 : bool operator()(const SortItem &xval, const SortItem &yval)
1117 : {
1118 47 : if(x->valtype != Value_CString)
1119 : {
1120 14 : x->valtype = Value_CString;
1121 : }
1122 47 : cleancode(*x);
1123 47 : x->val.code = reinterpret_cast<const uint *>(xval.str);
1124 47 : if(y->valtype != Value_CString)
1125 : {
1126 14 : y->valtype = Value_CString;
1127 : }
1128 47 : cleancode(*y);
1129 47 : y->val.code = reinterpret_cast<const uint *>(yval.str);
1130 47 : return executebool(body);
1131 : }
1132 : };
1133 :
1134 18 : if(x == y || x->type != Id_Alias || y->type != Id_Alias)
1135 : {
1136 4 : return;
1137 : }
1138 14 : std::vector<SortItem> items;
1139 14 : size_t clen = std::strlen(list),
1140 14 : total = 0;
1141 14 : char *cstr = newstring(list, clen);
1142 14 : const char *curlist = list,
1143 : *start, *end, *quotestart, *quoteend;
1144 59 : while(parselist(curlist, start, end, quotestart, quoteend))
1145 : {
1146 45 : cstr[end - list] = '\0';
1147 45 : SortItem item = { &cstr[start - list], quotestart, quoteend };
1148 45 : items.push_back(item);
1149 45 : total += item.quotelength();
1150 : }
1151 14 : if(items.empty())
1152 : {
1153 0 : commandret->setstr(cstr);
1154 0 : return;
1155 : }
1156 : identstack xstack, ystack;
1157 14 : pusharg(*x, NullVal(), xstack);
1158 14 : x->flags &= ~Idf_Unknown;
1159 14 : pusharg(*y, NullVal(), ystack);
1160 14 : y->flags &= ~Idf_Unknown;
1161 14 : size_t totalunique = total,
1162 14 : numunique = items.size();
1163 14 : if(body)
1164 : {
1165 8 : SortFunction f = { x, y, body };
1166 8 : std::sort(items.begin(), items.end(), f);
1167 8 : if((*unique&Code_OpMask) != Code_Exit)
1168 : {
1169 3 : f.body = unique;
1170 3 : totalunique = items[0].quotelength();
1171 3 : numunique = 1;
1172 12 : for(uint i = 1; i < items.size(); i++)
1173 : {
1174 9 : SortItem &item = items[i];
1175 9 : if(f(items[i-1], item))
1176 : {
1177 3 : item.quotestart = nullptr;
1178 : }
1179 : else
1180 : {
1181 6 : totalunique += item.quotelength();
1182 6 : numunique++;
1183 : }
1184 : }
1185 : }
1186 : }
1187 : else
1188 : {
1189 6 : SortFunction f = { x, y, unique };
1190 6 : totalunique = items[0].quotelength();
1191 6 : numunique = 1;
1192 18 : for(uint i = 1; i < items.size(); i++)
1193 : {
1194 12 : SortItem &item = items[i];
1195 21 : for(uint j = 0; j < i; ++j)
1196 : {
1197 14 : SortItem &prev = items[j];
1198 14 : if(prev.quotestart && f(item, prev))
1199 : {
1200 5 : item.quotestart = nullptr;
1201 5 : break;
1202 : }
1203 : }
1204 12 : if(item.quotestart)
1205 : {
1206 7 : totalunique += item.quotelength();
1207 7 : numunique++;
1208 : }
1209 : }
1210 : }
1211 14 : poparg(*x);
1212 14 : poparg(*y);
1213 14 : char *sorted = cstr;
1214 14 : size_t sortedlen = totalunique + std::max(numunique - 1, size_t(0));
1215 14 : if(clen < sortedlen)
1216 : {
1217 0 : delete[] cstr;
1218 0 : sorted = newstring(sortedlen);
1219 : }
1220 14 : int offset = 0;
1221 59 : for(uint i = 0; i < items.size(); i++)
1222 : {
1223 45 : SortItem &item = items[i];
1224 45 : if(!item.quotestart)
1225 : {
1226 8 : continue;
1227 : }
1228 37 : int len = item.quotelength();
1229 37 : if(i)
1230 : {
1231 23 : sorted[offset++] = ' ';
1232 : }
1233 37 : std::memcpy(&sorted[offset], item.quotestart, len);
1234 37 : offset += len;
1235 : }
1236 14 : sorted[offset] = '\0';
1237 14 : commandret->setstr(sorted);
1238 14 : }
1239 :
1240 1 : void initmathcmds()
1241 : {
1242 : //integer and boolean operators, used with named symbol, i.e. + or *
1243 : //no native boolean type, they are treated like integers
1244 114 : addcommand("+", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val + val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val + val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command); //0 substituted if nothing passed in arg2: n + 0 is still n
1245 6 : addcommand("*", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val * val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val * val2; } } else { val = numargs > 0 ? args[0].i : 1; ; } intret(val); }; }), "i" "1V", Id_Command); //1 substituted if nothing passed in arg2: n * 1 is still n
1246 6 : addcommand("-", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val - val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val - val2; } } else { val = numargs > 0 ? args[0].i : 0; val = -val; } intret(val); }; }), "i" "1V", Id_Command); //the minus operator inverts if used as unary
1247 13 : addcommand("=", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i == args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i == args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) == 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1248 7 : addcommand("!=", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i != args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i != args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) != 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1249 16 : addcommand("<", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i < args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i < args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) < 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1250 9 : addcommand(">", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i > args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i > args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) > 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1251 10 : addcommand("<=", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i <= args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i <= args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) <= 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1252 9 : addcommand(">=", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].i >= args[1].i; for(int i = 2; i < numargs && val; i++) { val = args[i-1].i >= args[i].i; } } else { val = (numargs > 0 ? args[0].i : 0) >= 0; } intret(static_cast<int>(val)); }; }), "i" "1V", Id_Command);
1253 16 : addcommand("^", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^ val2; } } else { val = numargs > 0 ? args[0].i : 0; val = ~val; } intret(val); }; }), "i" "1V", Id_Command);
1254 2 : addcommand("~", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^ val2; } } else { val = numargs > 0 ? args[0].i : 0; val = ~val; } intret(val); }; }), "i" "1V", Id_Command);
1255 19 : addcommand("&", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val & val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val & val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1256 16 : addcommand("|", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val | val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val | val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1257 17 : addcommand("^~", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val ^~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val ^~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1258 19 : addcommand("&~", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val &~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val &~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1259 17 : addcommand("|~", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val |~ val2; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val |~ val2; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1260 21 : addcommand("<<", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val = val2 < 32 ? val << std::max(val2, 0) : 0; for(int i = 2; i < numargs; i++) { val2 = args[i].i; val = val2 < 32 ? val << std::max(val2, 0) : 0; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1261 27 : addcommand(">>", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; val >>= std::clamp(val2, 0, 31); for(int i = 2; i < numargs; i++) { val2 = args[i].i; val >>= std::clamp(val2, 0, 31); } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1262 :
1263 : //floating point operators, used with <operator>f, i.e. +f or *f
1264 6 : addcommand("+" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val + val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val + val2; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1265 6 : addcommand("*" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val * val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val * val2; } } else { val = numargs > 0 ? args[0].f : 1; ; } floatret(val); }; }), "f" "1V", Id_Command);
1266 6 : addcommand("-" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = val - val2; for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = val - val2; } } else { val = numargs > 0 ? args[0].f : 0; val = -val; } floatret(val); }; }), "f" "1V", Id_Command);
1267 13 : addcommand("=" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f == args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f == args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) == 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1268 7 : addcommand("!=" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f != args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f != args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) != 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1269 16 : addcommand("<" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f < args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f < args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) < 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1270 9 : addcommand(">" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f > args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f > args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) > 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1271 10 : addcommand("<=" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f <= args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f <= args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) <= 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1272 9 : addcommand(">=" "f", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = args[0].f >= args[1].f; for(int i = 2; i < numargs && val; i++) { val = args[i-1].f >= args[i].f; } } else { val = (numargs > 0 ? args[0].f : 0) >= 0; } intret(static_cast<int>(val)); }; }), "f" "1V", Id_Command);
1273 :
1274 1 : addcommand("!", reinterpret_cast<identfun>(+[] (tagval *a) { intret(getbool(*a) ? 0 : 1); }), "t", Id_Not);
1275 1 : addcommand("&&", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { if(!numargs) { intret(1); } else { for(int i = 0; i < numargs; ++i) { if(i) { freearg(*commandret); } if(args[i].type == Value_Code) { executeret(args[i].code, *commandret); } else { *commandret = args[i]; } if(!getbool(*commandret)) { break; } } } }; }), "E1V", Id_And);
1276 :
1277 1 : addcommand("||", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { if(!numargs) { intret(0); } else { for(int i = 0; i < numargs; ++i) { if(i) { freearg(*commandret); } if(args[i].type == Value_Code) { executeret(args[i].code, *commandret); } else { *commandret = args[i]; } if(getbool(*commandret)) { break; } } } }; }), "E1V", Id_Or);
1278 :
1279 : //int division
1280 8 : addcommand("div", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; { if(val2) val /= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].i; { if(val2) val /= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1281 12 : addcommand("mod", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val; if(numargs >= 2) { val = args[0].i; int val2 = args[1].i; { if(val2) val %= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].i; { if(val2) val %= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].i : 0; ; } intret(val); }; }), "i" "1V", Id_Command);
1282 : //float division
1283 8 : addcommand("divf", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; { if(val2) val /= val2; else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].f; { if(val2) val /= val2; else val = 0; }; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1284 12 : addcommand("modf", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; { if(val2) val = std::fmod(val, val2); else val = 0; }; for(int i = 2; i < numargs; i++) { val2 = args[i].f; { if(val2) val = std::fmod(val, val2); else val = 0; }; } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1285 13 : addcommand("pow", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val; if(numargs >= 2) { val = args[0].f; float val2 = args[1].f; val = std::pow(val, val2); for(int i = 2; i < numargs; i++) { val2 = args[i].f; val = std::pow(val, val2); } } else { val = numargs > 0 ? args[0].f : 0; ; } floatret(val); }; }), "f" "1V", Id_Command);
1286 :
1287 : //float transcendentals
1288 7 : addcommand("sin", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::sin(*a/RAD)); }), "f", Id_Command);
1289 7 : addcommand("cos", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::cos(*a/RAD)); }), "f", Id_Command);
1290 7 : addcommand("tan", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::tan(*a/RAD)); }), "f", Id_Command);
1291 7 : addcommand("asin", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::asin(*a)*RAD); }), "f", Id_Command);
1292 7 : addcommand("acos", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::acos(*a)*RAD); }), "f", Id_Command);
1293 7 : addcommand("atan", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::atan(*a)*RAD); }), "f", Id_Command);
1294 7 : addcommand("atan2", reinterpret_cast<identfun>(+[] (float *y, float *x) { floatret(std::atan2(*y, *x)*RAD); }), "ff", Id_Command);
1295 7 : addcommand("sqrt", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::sqrt(*a)); }), "f", Id_Command);
1296 8 : addcommand("loge", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::log(*a)); }), "f", Id_Command);
1297 7 : addcommand("log2", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::log(*a)/M_LN2); }), "f", Id_Command);
1298 7 : addcommand("log10", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::log10(*a)); }), "f", Id_Command);
1299 7 : addcommand("exp", reinterpret_cast<identfun>(+[] (float *a) { floatret(std::exp(*a)); }), "f", Id_Command);
1300 :
1301 12 : addcommand("min", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val = numargs > 0 ? args[0].i : 0; for(int i = 1; i < numargs; i++) { val = std::min(val, args[i].i); } intret(val); }; }), "i" "1V", Id_Command);
1302 12 : addcommand("max", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val = numargs > 0 ? args[0].i : 0; for(int i = 1; i < numargs; i++) { val = std::max(val, args[i].i); } intret(val); }; }), "i" "1V", Id_Command);
1303 11 : addcommand("minf", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val = numargs > 0 ? args[0].f : 0; for(int i = 1; i < numargs; i++) { val = std::min(val, args[i].f); } floatret(val); }; }), "f" "1V", Id_Command);
1304 11 : addcommand("maxf", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val = numargs > 0 ? args[0].f : 0; for(int i = 1; i < numargs; i++) { val = std::max(val, args[i].f); } floatret(val); }; }), "f" "1V", Id_Command);
1305 :
1306 7 : addcommand("bitscan", reinterpret_cast<identfun>(+[] (int *n) { intret(BITSCAN(*n)); }), "i", Id_Command);
1307 :
1308 7 : addcommand("abs", reinterpret_cast<identfun>(+[] (int *n) { intret(std::abs(*n)); }), "i", Id_Command);
1309 7 : addcommand("absf", reinterpret_cast<identfun>(+[] (float *n) { floatret(std::fabs(*n)); }), "f", Id_Command);
1310 :
1311 8 : addcommand("floor", reinterpret_cast<identfun>(+[] (float *n) { floatret(std::floor(*n)); }), "f", Id_Command);
1312 8 : addcommand("ceil", reinterpret_cast<identfun>(+[] (float *n) { floatret(std::ceil(*n)); }), "f", Id_Command);
1313 16 : addcommand("round", reinterpret_cast<identfun>(+[] (float *n, float *k) { { double step = *k; double r = *n; if(step > 0) { r += step * (r < 0 ? -0.5 : 0.5); r -= std::fmod(r, step); } else { r = r < 0 ? std::ceil(r - 0.5) : std::floor(r + 0.5); } floatret(static_cast<float>(r)); }; }), "ff", Id_Command);
1314 :
1315 1 : addcommand("cond", reinterpret_cast<identfun>(+[] (tagval *args, int numargs)
1316 : {
1317 14 : for(int i = 0; i < numargs; i += 2)
1318 : {
1319 12 : if(i+1 < numargs) //if not the last arg
1320 : {
1321 10 : if(executebool(args[i].code))
1322 : {
1323 4 : executeret(args[i+1].code, *commandret);
1324 4 : break;
1325 : }
1326 : }
1327 : else
1328 : {
1329 2 : executeret(args[i].code, *commandret);
1330 2 : break;
1331 : }
1332 : }
1333 1 : }), "ee2V", Id_Command);
1334 :
1335 2 : addcommand("case", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { int val = args[0].getint(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || args[i].getint() == val) { executeret(args[i+1].code, *commandret); return; } } }; }), "i" "te2V", Id_Command);
1336 2 : addcommand("casef", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { float val = args[0].getfloat(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || args[i].getfloat() == val) { executeret(args[i+1].code, *commandret); return; } } }; }), "f" "te2V", Id_Command);
1337 2 : addcommand("cases", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { const char * val = args[0].getstr(); int i; for(i = 1; i+1 < numargs; i += 2) { if(args[i].type == Value_Null || !std::strcmp(args[i].getstr(), val)) { executeret(args[i+1].code, *commandret); return; } } }; }), "s" "te2V", Id_Command);
1338 :
1339 2 : addcommand("rnd", reinterpret_cast<identfun>(+[] (int *a, int *b) { intret(*a - *b > 0 ? randomint(*a - *b) + *b : *b); }), "ii", Id_Command);
1340 2 : addcommand("rndstr", reinterpret_cast<identfun>(+[] (int *len) { { int n = std::clamp(*len, 0, 10000); char *s = newstring(n); for(int i = 0; i < n;) { int r = rand(); for(int j = std::min(i + 4, n); i < j; i++) { s[i] = (r%255) + 1; r /= 255; } } s[n] = '\0'; stringret(s); }; }), "i", Id_Command);
1341 :
1342 17 : addcommand("tohex", reinterpret_cast<identfun>(+[] (int *n, int *p) { { constexpr int len = 20; char *buf = newstring(len); nformatstring(buf, len, "0x%.*X", std::max(*p, 1), *n); stringret(buf); }; }), "ii", Id_Command);
1343 :
1344 2 : addcommand("strcmp", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) == 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) == 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) == 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1345 40 : addcommand("=s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) == 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) == 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) == 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1346 5 : addcommand("!=s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) != 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) != 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) != 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1347 17 : addcommand("<s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) < 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) < 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) < 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1348 9 : addcommand(">s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) > 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) > 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) > 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1349 8 : addcommand("<=s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) <= 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) <= 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) <= 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1350 8 : addcommand(">=s", reinterpret_cast<identfun>(+[] (tagval *args, int numargs) { { bool val; if(numargs >= 2) { val = std::strcmp(args[0].s, args[1].s) >= 0; for(int i = 2; i < numargs && val; i++) { val = std::strcmp(args[i-1].s, args[i].s) >= 0; } } else { val = (numargs > 0 ? args[0].s[0] : 0) >= 0; } intret(static_cast<int>(val)); }; }), "s1V", Id_Command);
1351 1 : }
1352 :
1353 7 : char *strreplace(const char *s, const char *oldval, const char *newval, const char *newval2)
1354 : {
1355 7 : std::vector<char> buf;
1356 :
1357 7 : int oldlen = std::strlen(oldval);
1358 7 : if(!oldlen)
1359 : {
1360 2 : return newstring(s);
1361 : }
1362 5 : for(int i = 0;; i++)
1363 : {
1364 19 : const char *found = std::strstr(s, oldval);
1365 19 : if(found)
1366 : {
1367 25 : while(s < found)
1368 : {
1369 11 : buf.push_back(*s++);
1370 : }
1371 29 : for(const char *n = i&1 ? newval2 : newval; *n; n++)
1372 : {
1373 15 : buf.push_back(*n);
1374 : }
1375 14 : s = found + oldlen;
1376 : }
1377 : else
1378 : {
1379 27 : while(*s)
1380 : {
1381 22 : buf.push_back(*s++);
1382 : }
1383 5 : buf.push_back('\0');
1384 5 : return newstring(buf.data(), buf.size());
1385 : }
1386 14 : }
1387 7 : }
1388 :
1389 : //external api function, for loading the string manip functions into the global hashtable
1390 1 : void initstrcmds()
1391 : {
1392 3 : addcommand("echo", reinterpret_cast<identfun>(+[] (char *s) { conoutf("^f1%s", s); }), "C", Id_Command);
1393 2 : addcommand("error", reinterpret_cast<identfun>(+[] (char *s) { conoutf(Console_Error, "%s", s); }), "C", Id_Command);
1394 6 : addcommand("strstr", reinterpret_cast<identfun>(+[] (char *a, char *b) { { char *s = std::strstr(a, b); intret(s ? s-a : -1); }; }), "ss", Id_Command);
1395 6 : addcommand("strlen", reinterpret_cast<identfun>(+[] (char *s) { intret(std::strlen(s)); }), "s", Id_Command);
1396 :
1397 2 : addcommand("strlower", reinterpret_cast<identfun>(+[] (char *s) { { int len = std::strlen(s); char *m = newstring(len); for(int i = 0; i < static_cast<int>(len); ++i) { m[i] = cubelower(s[i]); } m[len] = '\0'; stringret(m); }; }), "s", Id_Command);
1398 2 : addcommand("strupper", reinterpret_cast<identfun>(+[] (char *s) { { int len = std::strlen(s); char *m = newstring(len); for(int i = 0; i < static_cast<int>(len); ++i) { m[i] = cubeupper(s[i]); } m[len] = '\0'; stringret(m); }; }), "s", Id_Command);
1399 :
1400 14 : static auto strsplice = [] (const char *s, const char *vals, int *skip, int *count)
1401 : {
1402 14 : int slen = std::strlen(s),
1403 14 : vlen = std::strlen(vals),
1404 14 : offset = std::clamp(*skip, 0, slen),
1405 14 : len = std::clamp(*count, 0, slen - offset);
1406 14 : char *p = newstring(slen - len + vlen);
1407 14 : if(offset)
1408 : {
1409 6 : std::memcpy(p, s, offset);
1410 : }
1411 14 : if(vlen)
1412 : {
1413 11 : std::memcpy(&p[offset], vals, vlen);
1414 : }
1415 14 : if(offset + len < slen)
1416 : {
1417 8 : std::memcpy(&p[offset + vlen], &s[offset + len], slen - (offset + len));
1418 : }
1419 14 : p[slen - len + vlen] = '\0';
1420 14 : commandret->setstr(p);
1421 14 : };
1422 1 : addcommand("strsplice", reinterpret_cast<identfun>(+strsplice), "ssii", Id_Command);
1423 8 : addcommand("strreplace", reinterpret_cast<identfun>(+[] (char *s, char *o, char *n, char *n2) { commandret->setstr(strreplace(s, o, n, n2[0] ? n2 : n)); }), "ssss", Id_Command);
1424 :
1425 1 : static auto substr = [] (char *s, int *start, int *count, int *numargs)
1426 : {
1427 1 : int len = std::strlen(s),
1428 1 : offset = std::clamp(*start, 0, len);
1429 1 : commandret->setstr(newstring(&s[offset], *numargs >= 3 ? std::clamp(*count, 0, len - offset) : len - offset));
1430 1 : };
1431 1 : addcommand("substr", reinterpret_cast<identfun>(+substr), "siiN", Id_Command);
1432 :
1433 5 : static auto stripcolors = [] (char *s)
1434 : {
1435 5 : int len = std::strlen(s);
1436 5 : char *d = newstring(len);
1437 5 : filtertext(d, s, true, false, len);
1438 5 : stringret(d);
1439 5 : };
1440 1 : addcommand("stripcolors", reinterpret_cast<identfun>(+stripcolors), "s", Id_Command);
1441 2 : addcommand("appendword", reinterpret_cast<identfun>(+[] (ident *id, tagval *v) { append(id, v, false); }), "rt", Id_Command);
1442 :
1443 7 : static auto concat = [] (tagval *v, int n)
1444 : {
1445 7 : commandret->setstr(conc(v, n, true));
1446 7 : };
1447 1 : addcommand("concat", reinterpret_cast<identfun>(+concat), "V", Id_Command);
1448 1 : addcommand("concatword", reinterpret_cast<identfun>(concatword), "V", Id_Command);
1449 1 : addcommand("format", reinterpret_cast<identfun>(format), "V", Id_Command);
1450 1 : }
1451 :
1452 : struct sleepcmd
1453 : {
1454 : int delay, millis, flags;
1455 : char *command;
1456 : };
1457 : std::vector<sleepcmd> sleepcmds;
1458 :
1459 1 : void addsleep(int *msec, char *cmd)
1460 : {
1461 : sleepcmd s;
1462 1 : s.delay = std::max(*msec, 1);
1463 1 : s.millis = lastmillis;
1464 1 : s.command = newstring(cmd);
1465 1 : s.flags = identflags;
1466 1 : sleepcmds.push_back(s);
1467 1 : }
1468 :
1469 0 : void checksleep(int millis)
1470 : {
1471 0 : for(uint i = 0; i < sleepcmds.size(); i++)
1472 : {
1473 0 : sleepcmd &s = sleepcmds[i];
1474 0 : if(millis - s.millis >= s.delay)
1475 : {
1476 0 : char *cmd = s.command; // execute might create more sleep commands
1477 0 : s.command = nullptr;
1478 0 : int oldflags = identflags;
1479 0 : identflags = s.flags;
1480 0 : execute(cmd);
1481 0 : identflags = oldflags;
1482 0 : delete[] cmd;
1483 0 : if(sleepcmds.size() > i && !sleepcmds[i].command)
1484 : {
1485 0 : sleepcmds.erase(sleepcmds.begin() + i);
1486 0 : i--;
1487 : }
1488 : }
1489 : }
1490 0 : }
1491 :
1492 1 : void clearsleep(bool clearoverrides)
1493 : {
1494 1 : int len = 0;
1495 1 : for(sleepcmd &i : sleepcmds)
1496 : {
1497 0 : if(i.command)
1498 : {
1499 0 : if(clearoverrides && !(i.flags&Idf_Overridden))
1500 : {
1501 0 : sleepcmds[len++] = i;
1502 : }
1503 : else
1504 : {
1505 0 : delete[] i.command;
1506 : }
1507 : }
1508 : }
1509 1 : sleepcmds.resize(len);
1510 1 : }
1511 :
1512 1 : void clearsleep_(int *clearoverrides)
1513 : {
1514 1 : clearsleep(*clearoverrides!=0 || identflags&Idf_Overridden);
1515 1 : }
1516 :
1517 1 : void initcontrolcmds()
1518 : {
1519 1 : addcommand("exec", reinterpret_cast<identfun>(exec), "sb", Id_Command);
1520 1 : addcommand("escape", reinterpret_cast<identfun>(escapecmd), "s", Id_Command);
1521 1 : addcommand("unescape", reinterpret_cast<identfun>(unescapecmd), "s", Id_Command);
1522 1 : addcommand("writecfg", reinterpret_cast<identfun>(writecfg), "s", Id_Command);
1523 1 : addcommand("changedvars", reinterpret_cast<identfun>(changedvars), "", Id_Command);
1524 :
1525 1 : addcommand("if", reinterpret_cast<identfun>(+[] (tagval *cond, uint *t, uint *f) { executeret(getbool(*cond) ? t : f, *commandret); }), "tee", Id_If);
1526 7 : addcommand("?", reinterpret_cast<identfun>(+[] (tagval *cond, tagval *t, tagval *f) { result(*(getbool(*cond) ? t : f)); }), "tTT", Id_Command);
1527 :
1528 1 : addcommand("pushif", reinterpret_cast<identfun>(+[] (ident *id, tagval *v, uint *code)
1529 : {
1530 1 : if(id->type != Id_Alias || id->index < Max_Args)
1531 : {
1532 0 : return;
1533 : }
1534 1 : if(getbool(*v))
1535 : {
1536 : identstack stack;
1537 0 : pusharg(*id, *v, stack);
1538 0 : v->type = Value_Null;
1539 0 : id->flags &= ~Idf_Unknown;
1540 0 : executeret(code, *commandret);
1541 0 : poparg(*id);
1542 : }
1543 1 : }), "rTe", Id_Command);
1544 1 : addcommand("do", reinterpret_cast<identfun>(+[] (uint *body) { executeret(body, *commandret); }), "e", Id_Do);
1545 2 : addcommand("append", reinterpret_cast<identfun>(+[] (ident *id, tagval *v) { append(id, v, true); }), "rt", Id_Command);
1546 1 : addcommand("result", reinterpret_cast<identfun>(+[] (tagval *v) { { *commandret = *v; v->type = Value_Null; }; }), "T", Id_Result);
1547 :
1548 1 : addcommand("listlen", reinterpret_cast<identfun>(listlencmd), "s", Id_Command);
1549 1 : addcommand("at", reinterpret_cast<identfun>(at), "si1V", Id_Command);
1550 1 : addcommand("sublist", reinterpret_cast<identfun>(sublist), "siiN", Id_Command);
1551 1 : addcommand("listcount", reinterpret_cast<identfun>(listcount), "rse", Id_Command);
1552 1 : addcommand("listfind", reinterpret_cast<identfun>(listfind), "rse", Id_Command);
1553 1 : addcommand("listfind=", reinterpret_cast<identfun>(listfindeq), "sii", Id_Command);
1554 7 : addcommand("loop", reinterpret_cast<identfun>(+[] (ident *id, int *n, uint *body) { doloop(*id, 0, *n, 1, body); }), "rie", Id_Command);
1555 9 : addcommand("loop+", reinterpret_cast<identfun>(+[] (ident *id, int *offset, int *n, uint *body) { doloop(*id, *offset, *n, 1, body); }), "riie", Id_Command);
1556 8 : addcommand("loop*", reinterpret_cast<identfun>(+[] (ident *id, int *step, int *n, uint *body) { doloop(*id, 0, *n, *step, body); }), "riie", Id_Command);
1557 10 : addcommand("loop+*", reinterpret_cast<identfun>(+[] (ident *id, int *offset, int *step, int *n, uint *body) { doloop(*id, *offset, *n, *step, body); }), "riiie", Id_Command);
1558 30 : addcommand("loopconcat", reinterpret_cast<identfun>(+[] (ident *id, int *n, uint *body) { loopconc(*id, 0, *n, body, true); }), "rie", Id_Command);
1559 11 : addcommand("loopconcat+", reinterpret_cast<identfun>(+[] (ident *id, int *offset, int *n, uint *body) { loopconc(*id, *offset, *n, body, true); }), "riie", Id_Command);
1560 :
1561 2 : addcommand("while", reinterpret_cast<identfun>(+[] (uint *cond, uint *body) { while(executebool(cond)) execute(body); }), "ee", Id_Command);
1562 :
1563 1 : static auto looplist = [] (ident *id, const char *list, const uint *body)
1564 : {
1565 1 : if(id->type!=Id_Alias)
1566 : {
1567 0 : return;
1568 : }
1569 : identstack stack;
1570 1 : int n = 0;
1571 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n++)
1572 : {
1573 0 : setiter(*id, listelem(start, end, qstart), stack);
1574 0 : execute(body);
1575 : }
1576 1 : if(n)
1577 : {
1578 0 : poparg(*id);
1579 : }
1580 : };
1581 :
1582 1 : static auto looplist2 = [] (ident *id, ident *id2, const char *list, const uint *body)
1583 : {
1584 1 : if(id->type!=Id_Alias || id2->type!=Id_Alias)
1585 : {
1586 0 : return;
1587 : }
1588 : identstack stack, stack2;
1589 1 : int n = 0;
1590 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 2)
1591 : {
1592 0 : setiter(*id, listelem(start, end, qstart), stack);
1593 0 : setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
1594 0 : execute(body);
1595 : }
1596 1 : if(n)
1597 : {
1598 0 : poparg(*id);
1599 0 : poparg(*id2);
1600 : }
1601 : };
1602 :
1603 1 : static auto looplist3 = [] (ident *id, ident *id2, ident *id3, const char *list, const uint *body)
1604 : {
1605 1 : if(id->type!=Id_Alias || id2->type!=Id_Alias || id3->type!=Id_Alias)
1606 : {
1607 0 : return;
1608 : }
1609 : identstack stack, stack2, stack3;
1610 1 : int n = 0;
1611 1 : for(const char *s = list, *start, *end, *qstart; parselist(s, start, end, qstart); n += 3)
1612 : {
1613 0 : setiter(*id, listelem(start, end, qstart), stack);
1614 0 : setiter(*id2, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack2);
1615 0 : setiter(*id3, parselist(s, start, end, qstart) ? listelem(start, end, qstart) : newstring(""), stack3);
1616 0 : execute(body);
1617 : }
1618 1 : if(n)
1619 : {
1620 0 : poparg(*id);
1621 0 : poparg(*id2);
1622 0 : poparg(*id3);
1623 : }
1624 : };
1625 1 : addcommand("looplist", reinterpret_cast<identfun>(+looplist), "rse", Id_Command);
1626 1 : addcommand("looplist2", reinterpret_cast<identfun>(+looplist2), "rrse", Id_Command);
1627 1 : addcommand("looplist3", reinterpret_cast<identfun>(+looplist3), "rrrse", Id_Command);
1628 :
1629 1 : addcommand("listassoc=", reinterpret_cast<identfun>(listassoceq), "si", Id_Command);
1630 2 : addcommand("looplistconcat", reinterpret_cast<identfun>(+[] (ident *id, char *list, uint *body) { looplistconc(id, list, body, true); }), "rse", Id_Command);
1631 2 : addcommand("looplistconcatword", reinterpret_cast<identfun>(+[] (ident *id, char *list, uint *body) { looplistconc(id, list, body, false); }), "rse", Id_Command);
1632 1 : addcommand("prettylist", reinterpret_cast<identfun>(prettylist), "ss", Id_Command);
1633 13 : addcommand("indexof", reinterpret_cast<identfun>(+[] (char *list, char *elem) { intret(listincludes(list, elem, std::strlen(elem))); }), "ss", Id_Command);
1634 :
1635 1 : addcommand("listdel", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1636 : {
1637 : {
1638 7 : std::vector<char> p;
1639 20 : for(const char *start, *end, *qstart, *qend; parselist(list, start, end, qstart, qend);)
1640 : {
1641 13 : int len = end - start;
1642 13 : if(listincludes(elems, start, len) < 0)
1643 : {
1644 4 : if(!p.empty())
1645 : {
1646 1 : p.push_back(' ');
1647 : }
1648 26 : for(int i = 0; i < qend - qstart; ++i)
1649 : {
1650 22 : p.push_back(qstart[i]);
1651 : }
1652 : }
1653 : }
1654 7 : p.push_back('\0');
1655 7 : char * arr = new char[p.size()];
1656 7 : std::memcpy(arr, p.data(), p.size());
1657 7 : commandret->setstr(arr);
1658 7 : }
1659 8 : }), "ss", Id_Command);
1660 :
1661 1 : addcommand("listintersect", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1662 : {
1663 : {
1664 8 : std::vector<char> p;
1665 19 : for(const char *start, *end, *qstart, *qend; parselist(list, start, end, qstart, qend);)
1666 : {
1667 11 : int len = end - start;
1668 11 : if(listincludes(elems, start, len) >= 0)
1669 : {
1670 9 : if(!p.empty())
1671 : {
1672 3 : p.push_back(' ');
1673 : }
1674 58 : for(int i = 0; i < qend - qstart; ++i)
1675 : {
1676 49 : p.push_back(qstart[i]);
1677 : }
1678 : }
1679 : }
1680 8 : p.push_back('\0');
1681 8 : char * arr = new char[p.size()];
1682 8 : std::memcpy(arr, p.data(), p.size());
1683 8 : commandret->setstr(arr);
1684 8 : }
1685 9 : }), "ss", Id_Command);
1686 :
1687 1 : addcommand("listunion", reinterpret_cast<identfun>(+[] (const char *list, const char *elems)
1688 : {
1689 : {
1690 8 : std::vector<char> p;
1691 76 : for(size_t i = 0; i < std::strlen(list); ++i)
1692 : {
1693 68 : p.push_back(list[i]);
1694 : }
1695 19 : for(const char *start, *end, *qstart, *qend; parselist(elems, start, end, qstart, qend);)
1696 : {
1697 11 : int len = end - start;
1698 11 : if(listincludes(list, start, len) < 0)
1699 : {
1700 2 : if(!p.empty())
1701 : {
1702 2 : p.push_back(' ');
1703 : }
1704 14 : for(int i = 0; i < qend - qstart; ++i)
1705 : {
1706 12 : p.push_back(qstart[i]);
1707 : }
1708 : }
1709 : }
1710 8 : p.push_back('\0');
1711 8 : char * arr = new char[p.size()];
1712 8 : std::memcpy(arr, p.data(), p.size());
1713 8 : commandret->setstr(arr);
1714 8 : }
1715 9 : }), "ss", Id_Command);
1716 :
1717 1 : addcommand("loopfiles", reinterpret_cast<identfun>(loopfiles), "rsse", Id_Command);
1718 1 : addcommand("listsplice", reinterpret_cast<identfun>(listsplice), "ssii", Id_Command);
1719 1 : addcommand("findfile", reinterpret_cast<identfun>(findfile_), "s", Id_Command);
1720 1 : addcommand("sortlist", reinterpret_cast<identfun>(sortlist), "srree", Id_Command);
1721 9 : addcommand("uniquelist", reinterpret_cast<identfun>(+[] (char *list, ident *x, ident *y, uint *body) { sortlist(list, x, y, nullptr, body); }), "srre", Id_Command);
1722 2 : addcommand("getmillis", reinterpret_cast<identfun>(+[] (int *total) { intret(*total ? totalmillis : lastmillis); }), "i", Id_Command);
1723 1 : addcommand("sleep", reinterpret_cast<identfun>(addsleep), "is", Id_Command);
1724 1 : addcommand("clearsleep", reinterpret_cast<identfun>(clearsleep_), "i", Id_Command);
1725 1 : }
|