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