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